Compare commits

..

6 Commits

@ -1,9 +1,9 @@
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:13306/sz_admin_test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=true
url: jdbc:mysql://127.0.0.1:3306/sz_admin_test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: 123456
password: Yanfa2023@
hikari:
#连接池名
pool-name: HikariCP

@ -1,5 +1,5 @@
ZLMediaKit:
ip: 192.168.2.129
ip: 192.168.2.128
port: 8080
app: live
vhost: __defaultVhost__

@ -1,9 +1,9 @@
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:13306/sz_admin_test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=true
url: jdbc:mysql://127.0.0.1:3306/sz_admin_test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: 123456
password: Yanfa2023@
hikari:
#连接池名
pool-name: HikariCP

@ -1,9 +1,9 @@
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:13306/sz_admin_test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=true
url: jdbc:mysql://127.0.0.1:3306/sz_admin_test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: 123456
password: Yanfa2023@
hikari:
#连接池名
pool-name: HikariCP

@ -56,12 +56,6 @@ public class ApiResult<T> implements Serializable {
return apiResult;
}
// public static <T> ApiResult<T> error(T data) {
// ApiResult<T> apiResult = new ApiResult<>();
// apiResult.data = data;
// return apiResult;
// }
public static <T> ApiResult<T> success(T data, Object param) {
ApiResult<T> apiResult = new ApiResult<>();
apiResult.data = data;

@ -55,7 +55,7 @@ public class SaTokenConfig implements WebMvcConfigurer {
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// D:/work/images/save/1.jpg
registry.addResourceHandler("/save/**")
.addResourceLocations("file:D:/work/images/","file:D:/work/temp/");
.addResourceLocations("file:D:/work/images/");
}
}

@ -101,11 +101,6 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.im4java</groupId>
<artifactId>im4java</artifactId>
<version>1.4.0</version>
</dependency>
<!-- jna,ffmpeg的相关依赖-->
<dependency>
<groupId>cc.eguid</groupId>

@ -1,19 +1,13 @@
package com.sz;
import com.sz.admin.monitor.service.CameraService;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@ -22,14 +16,12 @@ import org.springframework.scheduling.annotation.EnableScheduling;
@EnableScheduling
public class AdminApplication {
private static final Logger log = LoggerFactory.getLogger(AdminApplication.class);
@Value("${app.version}")
private String appVersion;
@Getter
private static String version;
@PostConstruct
public void init() {
setVersion(appVersion); // 通过辅助方法设置静态字段
@ -54,6 +46,4 @@ public class AdminApplication {
System.out.println(result);
}
}

@ -1,28 +0,0 @@
package com.sz.admin.monitor.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
@Configuration
public class CameraThreadPoolConfig {
@Bean("cameraInspectionExecutor")
public Executor cameraInspectionExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心线程数:
executor.setCorePoolSize(10);
// 最大线程数:并发峰值
executor.setMaxPoolSize(50);
// 队列容量:如果超过核心线程数的任务,先放进队列排队
executor.setQueueCapacity(200);
// 线程前缀名,方便以后看日志排查问题
executor.setThreadNamePrefix("Camera-Inspect-");
// 拒绝策略:如果队列满了,由调用者所在线程(即定时任务主线程)自己去执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}

@ -11,8 +11,8 @@ public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setConnectTimeout(10000);
factory.setReadTimeout(10000);
factory.setConnectTimeout(5000);
factory.setReadTimeout(5000);
return new RestTemplate(factory);
}
}

@ -1,68 +0,0 @@
package com.sz.admin.monitor.controller;
import com.mybatisflex.core.query.QueryWrapper;
import com.sz.admin.monitor.pojo.dto.blurDetection.BlurDetectionDTO;
import com.sz.admin.monitor.pojo.dto.blurDetection.BlurDetectionUpdateDTO;
import com.sz.admin.monitor.pojo.po.BlurDetection;
import com.sz.admin.monitor.pojo.vo.blurDetection.BlurDetectionVO;
import com.sz.admin.monitor.service.BlurDetectionService;
import com.sz.core.common.entity.ApiResult;
import com.sz.core.util.BeanCopyUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* ClassName: BlurDetectionController
* Package: com.sz.admin.monitor.controller
* Description:
*/
@Tag(name = "模糊检测")
@RestController
@RequestMapping("blur-detection")
@RequiredArgsConstructor
public class BlurDetectionController {
private final BlurDetectionService blurDetectionService;
/**
*
*/
@Operation(summary = "新增模糊检测配置")
@PostMapping("/add")
public ApiResult<Boolean> add(@RequestBody BlurDetectionDTO blurDetectionDTO) {
BlurDetection detection = BeanCopyUtils.copy(blurDetectionDTO, BlurDetection.class);
return ApiResult.success(blurDetectionService.save(detection));
}
/**
*
*/
@Operation(summary = "更新配置信息")
@PutMapping("/update")
public ApiResult<Boolean> update(@RequestBody BlurDetectionUpdateDTO blurDetectionUpdateDTO) {
return ApiResult.success(blurDetectionService.updateById(BeanCopyUtils.copy(blurDetectionUpdateDTO, BlurDetection.class)));
}
@Operation(summary = "新增或者更新配置信息")
@PostMapping("/addOrUpdate")
public ApiResult<Boolean> addOrUpdate(@RequestBody BlurDetectionDTO blurDetectionDTO) {
return ApiResult.success(blurDetectionService.saveOrUpdateBlur(blurDetectionDTO));
}
@GetMapping("/get")
@Operation(summary = "根据摄像头id查询模糊配置")
public ApiResult<BlurDetectionVO> getById(@RequestParam Long id) {
QueryWrapper wrapper = blurDetectionService.query().eq(BlurDetection::getCameraId, id);
BlurDetection blurDetection = blurDetectionService.getOne(wrapper);
if (blurDetection == null) {
return ApiResult.success(null);
}
return ApiResult.success(BeanCopyUtils.copy(blurDetection, BlurDetectionVO.class));
}
}

@ -1,34 +0,0 @@
package com.sz.admin.monitor.controller;
import com.sz.admin.monitor.pojo.po.ComputerRoom;
import com.sz.admin.monitor.service.ComputerRoomService;
import com.sz.core.common.entity.ApiResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Profile;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* ClassName: ComputerRootController
* Package: com.sz.admin.monitor.controller
* Description:
*/
@Tag(name = "机房表")
@RestController
@RequestMapping("computerRoom")
@RequiredArgsConstructor
@Profile({"dev", "local", "preview"})
public class ComputerRoomController {
private final ComputerRoomService computerRoomService;
@Operation(summary = "获取变电站下的机房列表")
@GetMapping("/list")
public ApiResult<List<ComputerRoom>> getList(@RequestParam("substationId") Long substationId) {
return ApiResult.success(computerRoomService.getComputerRoomList(substationId));
}
}

@ -1,49 +0,0 @@
package com.sz.admin.monitor.controller;
import cn.dev33.satoken.annotation.SaIgnore;
import com.sz.admin.monitor.pojo.dto.dataModel.DataModelDTO;
import com.sz.admin.monitor.pojo.que.dataModel.DataModelQue;
import com.sz.admin.monitor.service.impl.DataModelServiceImpl;
import com.sz.core.common.entity.ApiResult;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* @author xq
* @description:
* @date 2026/3/14 12:35
*/
@Slf4j
@RestController
@RequestMapping("/data/model")
public class DataModelController {
@Autowired
private DataModelServiceImpl dataModelService;
@PostMapping("/getModelData")
@SaIgnore
public ApiResult<DataModelDTO> getModelData(@RequestBody DataModelQue que) {
return dataModelService.getModelData(que);
}
/**
*
*
* @param dataModelDTO
* @return
*/
@PostMapping("/saveOrUpdate/modelData")
public ApiResult saveOrUpdateModelData(
@RequestBody DataModelDTO dataModelDTO, HttpServletRequest request, HttpServletResponse response) {
return dataModelService.saveOrUpdateModelData(dataModelDTO, request, response);
}
@GetMapping("/test")
public ApiResult test() {
return ApiResult.success(dataModelService.getList());
}
}

@ -11,8 +11,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* ClassName: DeviceController
* Package: com.sz.admin.monitor.controller
@ -32,18 +30,10 @@ public class DeviceController {
return ApiResult.success(deviceService.refresh(id,deviceType));
}
// 设备登录
@Operation(summary = "设备登录")
@Operation(summary = "/设备登录")
@GetMapping("/login")
public ApiResult<?> deviceLogin(@RequestParam("id") Long id)
{
return ApiResult.success(deviceService.deviceLogin(id));
}
// 整个NVR的历史视频的算法分析
@Operation(summary = "整个NVR的历史视频的算法分析")
@GetMapping("/history/analysis")
public ApiResult<?> captureHistoryAndCompose(@RequestParam("ids") List<Long> ids)
{
deviceService.captureHistoryAndCompose(ids);
return ApiResult.success();
}
}

@ -1,67 +0,0 @@
package com.sz.admin.monitor.controller;
import cn.dev33.satoken.annotation.SaIgnore;
import com.sz.admin.monitor.pojo.dto.file.FileEntityDTO;
import com.sz.admin.monitor.pojo.dto.file.FileStorageDTO;
import com.sz.admin.monitor.pojo.que.file.FileStorageQue;
import com.sz.admin.monitor.service.impl.FileServiceImpl;
import com.sz.core.common.entity.ApiResult;
import com.sz.core.common.entity.PageResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
/**
* @author xq
* @description:
* @date 2026/2/2 22:31
*/
@RestController
@RequestMapping("/fileStorage")
public class FileController {
@Autowired
private FileServiceImpl fileService;
@GetMapping("/deleteFileById")
public ApiResult<String> deleteFileById(@RequestParam("id") String id){
return fileService.deleteFileById(id);
}
@PostMapping("/editFileNameById")
public ApiResult<String> editFileNameById(@RequestBody FileEntityDTO dto){
return fileService.editFileNameById(dto);
}
@PostMapping("/file/list")
public ApiResult<PageResult<FileEntityDTO>> getFileObjectList(@RequestBody FileStorageQue que){
return fileService.getFileObjectList(que);
}
@GetMapping("/getPreviewFile/{id}")
@SaIgnore
public ResponseEntity<byte[]> getPreviewFile(@PathVariable String id){
return fileService.getPreviewFile(id);
}
@GetMapping("/getFile")
@SaIgnore
public ApiResult<FileStorageDTO> getFile(@RequestParam("id") String id) {
return fileService.getFile(id);
}
@PostMapping("/saveFile")
@SaIgnore
public ApiResult<FileStorageDTO> saveFile(@RequestParam("file") MultipartFile file) {
return fileService.saveFile(file);
}
@GetMapping("/test")
public void test01(){
fileService.test01();
}
}

@ -1,7 +1,6 @@
package com.sz.admin.monitor.controller;
import com.sz.admin.monitor.pojo.dto.edgebox.AlgorithmTaskDTO;
import com.sz.admin.monitor.pojo.po.RecordFile;
import com.sz.admin.monitor.pojo.vo.watchful.WatchfulVO;
import com.sz.admin.monitor.service.IpChannelService;
import com.sz.admin.monitor.utils.AlgMediaConfigResponse;
@ -12,8 +11,6 @@ import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Profile;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* ClassName: IpChannelController
* Package: com.sz.admin.monitor.controller
@ -100,21 +97,4 @@ public class IpChannelController {
return ApiResult.success(ipChannelService.configAlg(algTaskConfigDto));
}
// 抓取历史视频的图片
@Operation(summary = "抓取历史视频的图片并进行对比")
@GetMapping("/capture/history")
public ApiResult<Void> captureHistoryAndCompose(@RequestParam("id") Long id) {
ipChannelService.captureHistoryAndCompose(id);
return ApiResult.success();
}
// 按照时间范围获取录像
@Operation(summary = "按照时间范围获取录像")
@GetMapping("/getVideoByTime")
public ApiResult<List<RecordFile>> getVideoByTime(@RequestParam("id") Long id,
@RequestParam("startTime") String startTime,
@RequestParam("endTime") String endTime) {
return ApiResult.success(ipChannelService.getVideoByTime(id, startTime, endTime));
}
}

@ -39,9 +39,4 @@ public class TreeController {
public ApiResult<List<TreeNodeVO>> getTreePreview() {
return ApiResult.success(treeService.buildTreePreview());
}
@Operation(summary = "获取变电站的机房的树型结构数据")
@GetMapping("/substation")
public ApiResult<List<TreeNodeVO>> getTreeSubstation() {
return ApiResult.success(treeService.buildTreeComputeRoom());
}
}

@ -13,8 +13,7 @@ public enum AlarmReportEnums {
FIRE_ALARM("Fire",2,"明烟明火报警"),
FACE_ID_ALARM("FaceId",3,"人脸识别报警"),
CAPTURE_FACE_ALARM("CaptureFace", 4, "抓拍到人脸报警"),
HELMET_ALARM("NoHelmet", 5, "未戴安全帽报警"),
BLUR_ALARM("Blur", 6, "模糊报警");
HELMET_ALARM("NoHelmet", 5, "未戴安全帽报警");
private final String alarmType;
private final Integer alarmCode;
private final String alarmDescription;

@ -0,0 +1,26 @@
package com.sz.admin.monitor.listener.event;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
/**
* ClassName: ApplicationEventListener
* Package: com.sz.admin.monitor.listener.event
* Description:
*/
//@Slf4j
//@Service
public class ApplicationEventListener {
/**
*
*
* @param event
*/
// 监听ApplicationContext 初始化完成后的事件
// @EventListener
public void onApplicationEvent(ContextRefreshedEvent event) {
//log.info("执行相关的操作");
}
}

@ -0,0 +1,105 @@
package com.sz.admin.monitor.listener.event;
import com.sz.admin.monitor.listener.mode.CameraDeleteEvent;
import com.sz.admin.monitor.mapper.EdgeBoxMapper;
import com.sz.admin.monitor.pojo.po.Camera;
import com.sz.admin.monitor.pojo.po.EdgeBox;
import com.sz.admin.monitor.pojo.vo.edgebox.AlgorithmTaskVO;
import com.sz.admin.monitor.service.AlgorithmTaskService;
import com.sz.admin.monitor.service.CameraService;
import com.sz.admin.monitor.service.impl.CameraServiceImpl;
import com.sz.admin.monitor.utils.AiBoxRequestUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* ClassName: CameraDeleteEventListener
* Package: com.sz.admin.monitor.listener.event
* Description:
*/
@Component
@Slf4j
public class CameraDeleteEventListener {
@Resource
private CameraService cameraService;
@Resource
private AlgorithmTaskService algorithmTaskService;
@Resource
private EdgeBoxMapper edgeBoxMapper;
@Resource
private AiBoxRequestUtil aiBoxRequestUtil;
@EventListener
@Async
public void handleCameraDelete(CameraDeleteEvent event) {
List<Long> ids = event.getCameraIds();
try {
List<Camera> cameraList = cameraService.listByIds(ids);
if (cameraList.isEmpty()) return;
List<AlgorithmTaskVO> taskList = algorithmTaskService.listByCameraIds(ids);
// 一次性查询出这批摄像头管理的盒子
Set<Long> boxIdset = cameraList
.stream()
.map(Camera::getBoxId)
.collect(Collectors.toSet());
Map<Long, EdgeBox> boxMap=new HashMap<>();
if(!boxIdset.isEmpty())
{
List<EdgeBox> edgeBoxList = edgeBoxMapper.selectListByIds(boxIdset);
boxMap = edgeBoxList.stream().collect(Collectors.toMap(EdgeBox::getId, edgeBox -> edgeBox));
}
try {
// 批量删除本地数据
cameraService.batchDeleteLocalData(ids, taskList);
} catch (Exception e) {
log.error("本地数据库删除失败,中止后续硬件清理逻辑以防数据割裂!", e);
return;
}
// 清理盒子的算法任务
for (AlgorithmTaskVO task : taskList) {
if(task!=null && task.getAlgTaskSession()!=null)
{
Camera camera = cameraList.stream().filter(c -> c.getId().equals(task.getCameraId()))
.findFirst()
.orElse(null);
if(camera==null) continue;
EdgeBox edgeBox = boxMap.get(camera.getBoxId());
if(edgeBox==null) continue;
try {
aiBoxRequestUtil.controlTask(edgeBox.getIp(), edgeBox.getPort(), task.getAlgTaskSession(), 0);
aiBoxRequestUtil.deleteTask(edgeBox.getIp(), edgeBox.getPort(), task.getAlgTaskSession());
} catch (Exception e) {
log.error("清理 AI 盒子任务失败摄像机ID: {}, 忽略该错误继续执行...", task.getCameraId(), e);
}
}
}
// 清理盒子的通道
for (Camera camera : cameraList) {
Long boxId = camera.getBoxId();
if (boxId == null) continue;
EdgeBox edgeBox = boxMap.get(boxId);
if (edgeBox == null) continue;
try {
aiBoxRequestUtil.deleteVideoChannel(edgeBox.getIp(), edgeBox.getPort(), camera.getId().toString());
} catch (Exception e) {
log.error("清理 AI 盒子视频通道失败摄像机ID: {}", camera.getId(), e);
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
}

@ -0,0 +1,18 @@
package com.sz.admin.monitor.listener.mode;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.List;
/**
* ClassName: CameraDeleteEvent
* Package: com.sz.admin.monitor.listener.mode
* Description:
*/
@Data
@AllArgsConstructor
public class CameraDeleteEvent {
// 携带需要删除的摄像头 ID 集合
private List<Long> cameraIds;
}

@ -1,12 +0,0 @@
package com.sz.admin.monitor.mapper;
import com.mybatisflex.core.BaseMapper;
import com.sz.admin.monitor.pojo.po.BlurDetection;
/**
* ClassName: BlurDetectionMapper
* Package: com.sz.admin.monitor.mapper
* Description:
*/
public interface BlurDetectionMapper extends BaseMapper<BlurDetection> {
}

@ -1,13 +0,0 @@
package com.sz.admin.monitor.mapper;
import com.mybatisflex.core.BaseMapper;
import com.sz.admin.monitor.pojo.po.CanvasCfgDO;
/**
* @description:
* @author xq
* @version 1.0
*/
public interface CanvasCfgMapper extends BaseMapper<CanvasCfgDO> {
}

@ -1,12 +0,0 @@
package com.sz.admin.monitor.mapper;
import com.mybatisflex.core.BaseMapper;
import com.sz.admin.monitor.pojo.po.ComputerRoom;
/**
* ClassName: ComputeRoomMapper
* Package: com.sz.admin.monitor.mapper
* Description:
*/
public interface ComputeRoomMapper extends BaseMapper<ComputerRoom> {
}

@ -1,8 +0,0 @@
package com.sz.admin.monitor.mapper;
import com.mybatisflex.core.BaseMapper;
import com.sz.admin.monitor.pojo.po.FileStorageDO;
public interface FileStorageMapper extends BaseMapper<FileStorageDO> {
}

@ -1,13 +0,0 @@
package com.sz.admin.monitor.mapper;
import com.mybatisflex.core.BaseMapper;
import com.sz.admin.monitor.pojo.po.GridCfgDO;
/**
* @description:
* @author xq
* @version 1.0
*/
public interface GridCfgMapper extends BaseMapper<GridCfgDO> {
}

@ -1,25 +0,0 @@
package com.sz.admin.monitor.mapper;
import com.mybatisflex.core.BaseMapper;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.update.UpdateChain;
import com.sz.admin.monitor.pojo.po.ModelContentDO;
import java.util.Set;
/**
* @description:
* @author xq
* @version 1.0
*/
public interface ModelContentMapper extends BaseMapper<ModelContentDO> {
default void deleteByRoomIds(Set<Long> roomIds)
{
QueryWrapper wrapper = QueryWrapper.create()
.select()
.from(ModelContentDO.class)
.where(ModelContentDO::getRoomId).in(roomIds);
this.deleteByQuery(wrapper);
}
}

@ -1,8 +0,0 @@
package com.sz.admin.monitor.mapper;
import com.mybatisflex.core.BaseMapper;
import com.sz.admin.monitor.pojo.po.Substation;
import com.sz.admin.monitor.pojo.po.UserModelContentDO;
public interface UserModelContentMapper extends BaseMapper<UserModelContentDO> {
}

@ -1,13 +0,0 @@
package com.sz.admin.monitor.pojo.dto.blurDetection;
import com.sz.admin.monitor.pojo.po.BlurDetection;
import lombok.Data;
/**
* ClassName: BlurDetectionDTO
* Package: com.sz.admin.monitor.pojo.dto.blurDetection
* Description:
*/
@Data
public class BlurDetectionDTO extends BlurDetection {
}

@ -1,37 +0,0 @@
package com.sz.admin.monitor.pojo.dto.blurDetection;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serial;
import java.time.LocalDateTime;
/**
* ClassName: BlurDetectionUpdateDTO
* Package: com.sz.admin.monitor.pojo.dto.blurDetection
* Description:
*/
@Data
@Schema(description = "BlurDetection更新DTO")
public class BlurDetectionUpdateDTO {
/**
*
*/
private Long id;
/**
* 0- 1-
*/
private Integer enable;
/**
* url
*/
private String url;
}

@ -1,122 +0,0 @@
package com.sz.admin.monitor.pojo.dto.dataModel;
import cn.hutool.core.bean.BeanUtil;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sz.admin.monitor.pojo.po.CanvasCfgDO;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
*
*
* @author xq
* @description:
* @date 2026/1/29 11:32
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CanvasCfgDTO {
/**
* id
*/
private String id;
/**
* id
*/
private String userId;
/**
*
*/
private Integer width;
/**
*
*/
private Integer height;
/**
*
*/
private Double scale;
/**
*
*/
private String color;
/**
*
*/
private String img;
/**
*
*/
private Boolean guide;
/**
*
*/
private Boolean adsorp;
/**
*
*/
@JsonProperty("adsorp_diff")
private Double adsorpDiff;
/**
* json
*/
@JsonProperty("transform_origin")
private CoordinatePoint transformOrigin;
/**
* json
*/
@JsonProperty("drag_offset")
private CoordinatePoint dragOffset;
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class CoordinatePoint {
private Integer x;
private Integer y;
public void setX(Integer x) {
this.x = x;
}
public void setY(Integer y) {
this.y = y; // 修正这里的错误赋值
}
}
/**
*
*
* @param dto
* @return
*/
public static CanvasCfgDO dtoConvertDO(CanvasCfgDTO dto) {
CanvasCfgDO canvasCfgDO = BeanUtil.copyProperties(dto, CanvasCfgDO.class);
ObjectMapper objectMapper = new ObjectMapper();
try {
String transformOrigin = objectMapper.writeValueAsString(dto.getTransformOrigin());
String dragOffset = objectMapper.writeValueAsString(dto.getDragOffset());
canvasCfgDO.setTransformOrigin(transformOrigin);
canvasCfgDO.setDragOffset(dragOffset);
return canvasCfgDO;
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
}

@ -1,51 +0,0 @@
package com.sz.admin.monitor.pojo.dto.dataModel;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @author xq
* @description:
* @date 2026/1/29 11:47
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class DataModelDTO {
/**
*
*/
private CanvasCfgDTO canvasCfg;
/**
* ID
*/
private Long substationId;
/**
*ID
*/
private Long roomId;
/**
*
*/
private GridCfgDTO gridCfg;
/**
*
*/
private List<ModelContentDTO> json;
/**
*
*/
private String menuType;
}

@ -1,53 +0,0 @@
package com.sz.admin.monitor.pojo.dto.dataModel;
import cn.hutool.core.bean.BeanUtil;
import com.sz.admin.monitor.pojo.po.GridCfgDO;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
*
* @description:
* @author xq
* @date 2026/1/29 11:35
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class GridCfgDTO {
/**
* id
*/
private String id;
/**
* id
*/
private String userId;
/**
*
*/
private Boolean enabled;
/**
*
*/
private Boolean align;
/**
*
*/
private Integer size;
/**
*
* @param dto
* @return
*/
public static GridCfgDO dtoConvertDO(GridCfgDTO dto){
GridCfgDO gridCfgDO = BeanUtil.copyProperties(dto, GridCfgDO.class);
return gridCfgDO;
}
}

@ -1,154 +0,0 @@
package com.sz.admin.monitor.pojo.dto.dataModel;
import cn.hutool.core.bean.BeanUtil;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sz.admin.monitor.pojo.po.ModelContentDO;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
/**
*
* @description:
* @author xq
* @version 1.0
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ModelContentDTO {
/**
* id
*/
private String id;
/**
*
*/
private Integer modelIndex=99;
/**
* ID
*/
private Long substationId;
/**
*ID
*/
private Long roomId;
/**
*
*/
private String title;
/**
*
*/
private String type;
/**
* json
*/
private Place binfo;
/**
*
*/
private Boolean resize;
/**
*
*/
private Boolean rotate;
/**
*
*/
private Boolean lock;
/**
*
*/
private Boolean active;
/**
*
*/
private Boolean hide;
/**
* json
*/
private Map<String,String> props;
/**
* vue
*/
private String tag;
/**
* json
*/
@JsonProperty("common_animations")
private Animation commonAnimations;
/**
*
*/
private List<String> events;
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class Place{
private BigDecimal left;
private BigDecimal top;
private BigDecimal width;
private BigDecimal height;
private BigDecimal angle;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class Animation{
private String val;
private String delay;
private String speed;
private String repeat;
}
/**
*
* @param dto
* @return
*/
public static ModelContentDO dtoConvertDO(ModelContentDTO dto){
ModelContentDO modelContentDO = BeanUtil.copyProperties(dto, ModelContentDO.class);
ObjectMapper objectMapper = new ObjectMapper();
try {
String binfo = objectMapper.writeValueAsString(dto.getBinfo());
String commonAnimations = objectMapper.writeValueAsString(dto.getCommonAnimations());
String props = objectMapper.writeValueAsString(dto.getProps());
modelContentDO.setBinfo(binfo);
modelContentDO.setCommonAnimations(commonAnimations);
modelContentDO.setProps(props);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
return modelContentDO;
}
}

@ -1,38 +0,0 @@
package com.sz.admin.monitor.pojo.dto.dataModel;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
/**
* @author xq
* @description:
* @date 2026/1/29 11:50
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserModelContentDTO {
/**
* id
*/
private String id;
/**
* id
*/
private String userId;
/**
*
*/
private String menuType;
/**
*
*/
private String modelContentId;
}

@ -23,14 +23,17 @@ public class BoxAlarmReportDto {
private String cameraNo;
@Schema(description = "摄像头ID")
private Long cameraId;
@Schema(description = "报警的url")
private String url;
@Schema(description = "报警的类型")
private String alarmTypeDesc;
@Schema(description = "报警的枚举值")
private Integer alarmType;
private String alarmReportType;
@Schema(description = "报警的时间")
private Long alarmTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime alarmReportTime;
@Schema(description = "基准图")
private String baseImage;
@Schema(description = "报警图")
private String alarmImage;
@Schema(description = "抓拍图")
private String captureImage;
@Schema(description = "报警的原因")
private String description;
}

@ -1,38 +0,0 @@
package com.sz.admin.monitor.pojo.dto.file;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author xq
* @description:
* @date 2026/2/3 14:27
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class FileEntityDTO {
/**
* id
*/
private String id;
/**
*
*/
private String fileName;
/**
*
*/
private Long addTime;
/**
* path
*/
private String filePath;
}

@ -1,61 +0,0 @@
package com.sz.admin.monitor.pojo.dto.file;
import cn.hutool.core.bean.BeanUtil;
import com.sz.admin.monitor.pojo.po.FileStorageDO;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
/**
* @author xq
* @description:
* @date 2026/2/2 22:18
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class FileStorageDTO {
/**
* id
*/
private String id;
/**
*
*/
private String fileUrl;
/**
*
*/
private String fileSize;
/**
*
*/
private String fileName;
/**
*
*/
private String fileSuffix;
/**
* base64
*/
private String base64Data;
/**
*
*/
private Date addTime;
public static FileStorageDO dtoConvertDO(FileStorageDTO dto) {
return BeanUtil.copyProperties(dto, FileStorageDO.class);
}
}

@ -72,7 +72,6 @@ public class AlgorithmTask implements Serializable {
private JsonNode userData;
/**
*
*/

@ -1,62 +0,0 @@
package com.sz.admin.monitor.pojo.po;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.Table;
import com.sz.mysql.EntityChangeListener;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* ClassName: BlurDetection
* Package: com.sz.admin.monitor.pojo.po
* Description:
*/
@Data
@Table(value = "ry_blur_detection", onInsert = EntityChangeListener.class, onUpdate = EntityChangeListener.class)
@Schema(description = "模糊检测表")
public class BlurDetection implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
*
*/
@Id(keyType = KeyType.Auto)
private Long id;
/**
* id
*/
private Long cameraId;
/**
*
*/
private String cameraName;
/**
* 0- 1-
*/
private Integer enable;
/**
* url
*/
private String url;
/**
*
*/
private LocalDateTime createTime;
/**
*
*/
private LocalDateTime updateTime;
}

@ -80,7 +80,6 @@ public class Camera implements Serializable {
@Schema(description = "在线状态1-在线, 0-离线")
private Integer status;
@Schema(description = "创建时间")
private LocalDateTime createTime;

@ -50,7 +50,7 @@ public class CameraAlarm implements Serializable {
@Schema(description = "告警区域id")
private Long alarmAreaId;
@Schema(description = "报警类型: 1-移位, 2-明烟明火报警,3-人脸识别,4-脸部抓拍,5-安全帽检测,6-模糊")
@Schema(description = "报警类型: 1-移位, 2-明烟明火报警,3-人脸识别,4-脸部抓拍,5-安全帽检测")
private Integer alarmType;
@Schema(description = "基准图路径 ")

@ -39,13 +39,14 @@ public class CameraSnapshot implements Serializable {
@Schema(description = "图片保存路径")
private String imagePath;
@Schema(description = "1-定时巡检, 2-守望归位检测, 3-手动抓拍,4-模糊检测")
@Schema(description = "1-定时巡检, 2-守望归位检测, 3-手动抓拍")
private Integer checkType;
@Schema(description = "0-正常, 1-移位")
private Integer isAbnormal;
@Schema(description = "抓拍时间")
private LocalDateTime createTime;
}

@ -1,159 +0,0 @@
package com.sz.admin.monitor.pojo.po;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.Table;
import com.sz.admin.monitor.pojo.dto.dataModel.CanvasCfgDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
*
* @description:
* @author xq
* @date 2026/1/29 11:32
*/
@Table("ry_canvas_cfg")
@Schema(description = "画布配置数据对象")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CanvasCfgDO {
/**
* id
*/
@Id(keyType = KeyType.Generator, value = "uuid")
@Schema(description = "面板id")
private String id;
/**
* id
*/
@Schema(description = "用户id")
private String userId;
/**
*
*/
@Schema(description = "宽度")
private Integer width;
/**
*
*/
@Schema(description = "高度")
private Integer height;
/**
*
*/
@Schema(description = "比例")
private Double scale;
/**
*
*/
@Schema(description = "颜色")
private String color;
/**
*
*/
@Schema(description = "图片")
private String img;
/**
*
*/
@Schema(description = "指南")
private Boolean guide;
/**
*
*/
@Schema(description = "吸附")
private Boolean adsorp;
/**
*
*/
@Schema(description = "吸附差分")
private Double adsorpDiff;
/**
* json
*/
@Schema(description = "原点json格式")
private String transformOrigin;
/**
* json
*/
@Schema(description = "拖拽json格式")
private String dragOffset;
/**
* DTO
* @param canvasCfgDO
* @return
*/
@Schema(description = "模型id")
public static CanvasCfgDTO doConvertDTO(CanvasCfgDO canvasCfgDO){
// 创建新的CanvasCfgDTO实例先复制其他属性
CanvasCfgDTO canvasCfgDTO = new CanvasCfgDTO();
// 手动复制除transformOrigin和dragOffset之外的所有基本类型属性
canvasCfgDTO.setId(canvasCfgDO.getId());
canvasCfgDTO.setUserId(canvasCfgDO.getUserId());
canvasCfgDTO.setWidth(canvasCfgDO.getWidth());
canvasCfgDTO.setHeight(canvasCfgDO.getHeight());
canvasCfgDTO.setScale(canvasCfgDO.getScale());
canvasCfgDTO.setColor(canvasCfgDO.getColor());
canvasCfgDTO.setImg(canvasCfgDO.getImg());
canvasCfgDTO.setGuide(canvasCfgDO.getGuide());
canvasCfgDTO.setAdsorp(canvasCfgDO.getAdsorp());
canvasCfgDTO.setAdsorpDiff(canvasCfgDO.getAdsorpDiff());
ObjectMapper objectMapper = new ObjectMapper();
String transformOrigin = canvasCfgDO.getTransformOrigin();
String dragOffset = canvasCfgDO.getDragOffset();
try {
// 单独处理JSON转换 - 添加空值检查
if (transformOrigin != null && !transformOrigin.trim().isEmpty() && !transformOrigin.equals("null")) {
CanvasCfgDTO.CoordinatePoint originPoint = objectMapper.readValue(transformOrigin, CanvasCfgDTO.CoordinatePoint.class);
canvasCfgDTO.setTransformOrigin(originPoint);
}
if (dragOffset != null && !dragOffset.trim().isEmpty() && !dragOffset.equals("null")) {
CanvasCfgDTO.CoordinatePoint dragPoint = objectMapper.readValue(dragOffset, CanvasCfgDTO.CoordinatePoint.class);
canvasCfgDTO.setDragOffset(dragPoint);
}
return canvasCfgDTO;
} catch (JsonProcessingException e) {
throw new RuntimeException("JSON parsing error: " + e.getMessage() + ", JSON: " + transformOrigin, e);
}
}
public static final String ID="id";
public static final String USER_ID="user_id";
public static final String WIDTH = "width";
public static final String HEIGHT = "height";
public static final String SCALE = "scale";
public static final String COLOR = "color";
public static final String IMG = "img";
public static final String GUIDE = "guide";
public static final String ADSORP = "adsorp";
public static final String ADSORP_DIFF = "adsorp_diff";
public static final String TRANSFORM_ORIGIN = "transform_origin";
public static final String DRAG_OFFSET = "drag_offset";
}

@ -1,28 +0,0 @@
package com.sz.admin.monitor.pojo.po;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.Table;
import com.sz.mysql.EntityChangeListener;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* ClassName: ComputerRoom
* Package: com.sz.admin.monitor.pojo.po
* Description:
*/
@Data
@Table(value = "ry_computer_room", onInsert = EntityChangeListener.class, onUpdate = EntityChangeListener.class)
@Schema(description = "机房表")
public class ComputerRoom {
@Id(keyType = KeyType.Auto)
@Schema(description = "id")
private Long id;
@Schema(description = "名称")
private String name;
@Schema(description = "描述")
private String description;
@Schema(description = "变电站ID")
private Long substationId;
}

@ -1,69 +0,0 @@
package com.sz.admin.monitor.pojo.po;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.Table;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
/**
* @author xq
* @description:
* @date 2026/2/2 22:18
*/
@Table("ry_file_storage")
@Schema(description = "文件存储数据对象")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class FileStorageDO {
/**
* id
*/
@Id(keyType = KeyType.Generator, value = "uuid")
@Schema(description = "主键ID")
private String id;
/**
*
*/
@Schema(description = "文件路径")
private String fileUrl;
/**
*
*/
@Schema(description = "文件大小")
private String fileSize;
/**
*
*/
@Schema(description = "文件名称")
private String fileName;
/**
*
*/
@Schema(description = "文件后缀")
private String fileSuffix;
/**
*
*/
@Schema(description = "添加时间")
private Date addTime;
public static final String ID="id";
public static final String FILE_URL="file_url";
public static final String FILE_SIZE="file_size";
public static final String FILE_NAME="file_name";
public static final String FILE_SUFFIX="file_suffix";
public static final String ADD_TIME="add_time";
}

@ -1,74 +0,0 @@
package com.sz.admin.monitor.pojo.po;
import cn.hutool.core.bean.BeanUtil;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.Table;
import com.sz.admin.monitor.pojo.dto.dataModel.GridCfgDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
*
* @description:
* @author xq
* @date 2026/1/29 11:35
*/
@Table("ry_grid_cfg")
@Schema(description = "存储网格的基本配置信息")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class GridCfgDO {
/**
* id
*/
@Id(keyType = KeyType.Generator, value = "uuid")
@Schema(description = "主键ID")
private String id;
/**
* id
*/
@Schema(description = "用户id")
private String userId;
/**
*
*/
@Schema(description = "网格是否启用")
private Boolean enabled;
/**
*
*/
@Schema(description = "对齐")
private Boolean align;
/**
*
*/
@Schema(description = "大小")
private Integer size;
/**
* DTO
* @param gridCfgDO
* @return
*/
public static GridCfgDTO doConvertDTO(GridCfgDO gridCfgDO){
GridCfgDTO gridCfgDTO = BeanUtil.copyProperties(gridCfgDO, GridCfgDTO.class);
return gridCfgDTO;
}
public static final String ID = "id";
public static final String USER_ID = "user_id";
public static final String ENABLED = "enabled";
public static final String ALIGN = "align";
public static final String SIZE = "size";
}

@ -1,188 +0,0 @@
package com.sz.admin.monitor.pojo.po;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.Table;
import com.sz.admin.monitor.pojo.dto.dataModel.ModelContentDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.HashMap;
/**
*
* @description:
* @author xq
* @version 1.0
*/
@Table("ry_model_content")
@Schema(description = "存储模型的基本信息和配置")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ModelContentDO {
/**
* id
*/
@Id(keyType = KeyType.Generator, value = "uuid")
@Schema(description = "模型id")
private String id;
/**
* ID
*/
@Schema(description = "变电站ID")
private Long substationId;
/**
*ID
*/
@Schema(description = "机房ID")
private Long roomId;
/**
*
*/
@Schema(description = "模型索引")
private Integer modelIndex=99;
/**
*
*/
@Schema(description = "模型名称")
private String title;
/**
*
*/
@Schema(description = "模型类型")
private String type;
/**
* json
*/
@Schema(description = "位置json格式")
private String binfo;
/**
*
*/
@Schema(description = "调整大小")
private Boolean resize;
/**
*
*/
@Schema(description = "旋转")
private Boolean rotate;
/**
*
*/
@Schema(description = "锁定")
private Boolean lock;
/**
*
*/
@Schema(description = "动作")
private Boolean active;
/**
*
*/
@Schema(description = "隐藏")
private Boolean hide;
/**
* json
*/
@Schema(description = "属性json格式")
private String props;
/**
* vue
*/
@Schema(description = "vue组件名称")
private String tag;
/**
* json
*/
@Schema(description = "动画json格式")
private String commonAnimations;
/**
*
*/
@Schema(description = "事件")
private String events;
public static final String ID = "id";
public static final String MODEL_INDEX = "model_index";
public static final String TITLE = "title";
public static final String TYPE = "type";
public static final String BINFO = "binfo";
public static final String RESIZE = "resize";
public static final String ROTATE = "rotate";
public static final String LOCK = "lock";
public static final String ACTIVE = "active";
public static final String HIDE = "hide";
public static final String PROPS = "props";
public static final String TAG = "tag";
public static final String COMMON_ANIMATIONS = "common_animations";
public static final String EVENTS = "events";
public static ModelContentDTO doConvertDTO(ModelContentDO modelContentDO) {
if (modelContentDO == null) {
return null;
}
ModelContentDTO modelContentDTO = new ModelContentDTO();
// 基础字段映射
modelContentDTO.setId(modelContentDO.getId());
modelContentDTO.setTitle(modelContentDO.getTitle());
modelContentDTO.setType(modelContentDO.getType());
modelContentDTO.setResize(modelContentDO.getResize());
modelContentDTO.setRotate(modelContentDO.getRotate());
modelContentDTO.setLock(modelContentDO.getLock());
modelContentDTO.setActive(modelContentDO.getActive());
modelContentDTO.setHide(modelContentDO.getHide());
modelContentDTO.setTag(modelContentDO.getTag());
modelContentDTO.setEvents(new ArrayList<>());
ObjectMapper objectMapper = new ObjectMapper();
try {
// JSON 字段转换 - 位置信息
if (modelContentDO.getBinfo() != null && !modelContentDO.getBinfo().trim().isEmpty() && !"null".equals(modelContentDO.getBinfo())) {
ModelContentDTO.Place binfoObj = objectMapper.readValue(modelContentDO.getBinfo(), ModelContentDTO.Place.class);
modelContentDTO.setBinfo(binfoObj);
}
// JSON 字段转换 - 属性
if (modelContentDO.getProps() != null && !modelContentDO.getProps().trim().isEmpty() && !"null".equals(modelContentDO.getProps())) {
HashMap propsObj = objectMapper.readValue(modelContentDO.getProps(), HashMap.class);
modelContentDTO.setProps(propsObj);
}
// JSON 字段转换 - 动画
if (modelContentDO.getCommonAnimations() != null && !modelContentDO.getCommonAnimations().trim().isEmpty() && !"null".equals(modelContentDO.getCommonAnimations())) {
ModelContentDTO.Animation animationsObj = objectMapper.readValue(modelContentDO.getCommonAnimations(), ModelContentDTO.Animation.class);
modelContentDTO.setCommonAnimations(animationsObj);
}
} catch (JsonProcessingException e) {
throw new RuntimeException("JSON parsing error in ModelContentDO conversion: " + e.getMessage(), e);
}
return modelContentDTO;
}
}

@ -45,7 +45,6 @@ public class Preset {
private String presetUrl;
@Schema(description = "创建时间")
private LocalDateTime createdTime;

@ -1,53 +0,0 @@
package com.sz.admin.monitor.pojo.po;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.Table;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
/**
* @description:
* @author xq
* @version 1.0
*/
@Table("ry_user_model_content")
@Schema(description = "用户模型中间表")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserModelContentDO implements Serializable {
/**
* id
*/
@Id(keyType = KeyType.Generator, value = "uuid")
@Schema(description = "主键ID")
private String id;
/**
* id
*/
@Schema(description = "用户id")
private String userId;
/**
*
*/
@Schema(description = "目录类型")
private String menuType;
/**
*
*/
@Schema(description = "模型内容")
private String modelContentId;
public static final String ID="id";
public static final String USER_ID="user_id";
public static final String MENU_TYPE="menu_type";
public static final String MODEL_CONTENT_ID="model_content_id";
}

@ -1,36 +0,0 @@
package com.sz.admin.monitor.pojo.que;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @author xq
* @description:
* @date 2026/2/3 14:21
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class BasePage<T> {
private List<T> list;
/**
*
*/
private long total;
/**
*
*/
private long size;
/**
*
*/
private long current;
/**
*
*/
private long pages;
}

@ -1,38 +0,0 @@
package com.sz.admin.monitor.pojo.que;
import cn.hutool.core.util.ObjectUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author xq
* @description:
* @date 2026/2/3 13:36
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class BaseQue {
/**
*
*/
private Integer pageNum;
/**
*
*/
private Integer pageSize;
/**
*
* @param base
*/
// public static <T> Page<T> init(BaseQue base,Class<T> cla){
// if(ObjectUtil.isEmpty(base))
// return null;
// base.setPageNum(ObjectUtil.isNotEmpty(base.getPageNum())?base.getPageNum():0);
// base.setPageSize(ObjectUtil.isNotEmpty(base.getPageSize())?base.getPageSize():10);
// return new Page<>(base.getPageNum(), base.getPageSize());
// }
}

@ -1,31 +0,0 @@
package com.sz.admin.monitor.pojo.que.dataModel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author xq
* @description:
* @date 2026/1/29 21:47
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class DataModelQue {
/**
* ID
*/
private String userId;
/**
*
*/
private String menuType;
/**
* ID
*/
private Long roomId;
}

@ -1,46 +0,0 @@
package com.sz.admin.monitor.pojo.que.file;
import com.sz.admin.monitor.pojo.que.BaseQue;
import com.sz.core.common.entity.PageQuery;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.util.Date;
/**
* @author xq
* @description:
* @date 2026/2/3 13:35
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@Schema(description = "文件查询Que")
public class FileStorageQue extends PageQuery {
/**
* id
*/
@Schema(description = "id")
private String id;
/**
*
*/
@Schema(description = "文件名称")
private String fileName;
/**
*
*/
@Schema(description = "开始时间")
private Long startTime;
/**
*
*/
@Schema(description = "结束时间")
private Long endTime;
}

@ -1,56 +0,0 @@
package com.sz.admin.monitor.pojo.vo.blurDetection;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serial;
import java.time.LocalDateTime;
/**
* ClassName: BlurDetectionVO
* Package: com.sz.admin.monitor.pojo.vo.blurDetection
* Description:
*/
@Data
@Schema(description = "BlurDetection返回vo")
public class BlurDetectionVO {
/**
*
*/
private Long id;
/**
* id
*/
private Long cameraId;
/**
*
*/
private String cameraName;
/**
* 0- 1-
*/
private Integer enable;
/**
* url
*/
private String url;
/**
*
*/
private LocalDateTime createTime;
/**
*
*/
private LocalDateTime updateTime;
}

@ -32,7 +32,7 @@ public class CameraSnapshotVO {
@Schema(description = "图片保存路径")
private String imagePath;
@Schema(description = "1-定时巡检, 2-守望归位检测, 3-手动抓拍,4-模糊检测")
@Schema(description = "1-定时巡检, 2-守望归位检测, 3-手动抓拍")
private Integer checkType;
@Schema(description = "0-正常, 1-移位")

@ -1,13 +0,0 @@
package com.sz.admin.monitor.pojo.vo.tree;
import lombok.Data;
/**
* ClassName: ComputerRoomNodeVO
* Package: com.sz.admin.monitor.pojo.vo.tree
* Description:
*/
@Data
public class ComputerRoomNodeVO extends TreeNodeVO{
private Long substationId; //所属变电站
}

@ -17,7 +17,7 @@ import java.util.List;
public class TreeNodeVO {
private Long id;
private String name;
private Integer type; // 0: Region, 1: Substation, 2: NVR,3: Camera,4: room
private Integer type; // 0: Region, 1: Substation, 2: NVR,3: Camera
private List<TreeNodeVO> children;
public TreeNodeVO(Long id, String name, Integer type) {
this.id = id;

@ -1,7 +1,5 @@
package com.sz.admin.monitor.sdk;
import com.sz.admin.monitor.sdk.hkSdk.HCNetSDK;
/**
* ClassName: AbstractNVR
* Package: com.sz.admin.monitor.hik
@ -41,14 +39,4 @@ public abstract class AbstractNVR {
* @return true false
*/
public abstract String capturePic(int luerId,int iChannel,String fileName);
/**
*
*/
public abstract boolean captureHistoryImage(int luerId, int iChannel,
HCNetSDK.NET_DVR_TIME startDvrTime,
HCNetSDK.NET_DVR_TIME stopDvrTime,
String tempVideoPath,
String finalImagePath);
}

@ -1,8 +1,6 @@
package com.sz.admin.monitor.sdk;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.RandomUtil;
import com.mybatisflex.core.query.QueryWrapper;
import com.sz.admin.monitor.pojo.po.Camera;
import com.sz.admin.monitor.pojo.po.Nvr;
@ -36,10 +34,6 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
/**
* NVR
@ -62,14 +56,13 @@ public class ManageNVR {
private VcrlUserIdContainer vcrlUserIdContainer;
@Resource
private DataScopeProperties dataScopeProperties;
private final ExecutorService executor = Executors.newFixedThreadPool(5);
/**
* sdknvr
*/
@Bean
public Object nvrInit() {
// //海康sdk注册
//海康sdk注册
HCNetSDK hcNetSDK = hkNVR.registerPlugin();
Nvr nvr = new Nvr();
nvr.setDriver(0);
@ -756,124 +749,4 @@ public class ManageNVR {
Map<String, Integer> map = hkNVR.NET_DVR_GetPTZPos(userId, camera.getChannelId());
return BeanCopyUtils.copy(map, PTZVO.class);
}
// 进行历史视频的抓图
public void captureHistoryImage(String taskDir, Long id) {
Camera camera = cameraService.getById(id);
Integer userId = (Integer) vcrlUserIdContainer.getlUserId(camera.getNvrId().intValue());
// 收集前一个月的每个小时的时间点
List<LocalDateTime> points = new ArrayList<>();
LocalDateTime endTime = LocalDateTime.now().withMinute(0).withSecond(0);
LocalDateTime startTime = endTime.minusDays(7);
LocalDateTime current = startTime;
while (current.isBefore(endTime) || current.equals(endTime)) {
points.add(current);
current = current.plusHours(1);
}
int totalSizes = points.size();
// 创建固定大小的线程池
ExecutorService threadPool = Executors.newFixedThreadPool(5);
// 统计进度的原子类
AtomicInteger successCount = new AtomicInteger(0);
AtomicInteger failCount = new AtomicInteger(0);
AtomicInteger finishedCount = new AtomicInteger(0);
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss");
List<CompletableFuture<Void>> futures = new ArrayList<>();
// 将任务全部丢进线程池
for (LocalDateTime time : points) {
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
String fileName = "history_"+camera.getId()+ time.format(fmt) + ".jpg";
File file = captureAtTime(userId, camera.getChannelId(), time, taskDir, fileName);
// 记录当前完成的次数
int currentFinished = finishedCount.incrementAndGet();
if (file != null) {
successCount.incrementAndGet();
// System.out.println(String.format("[进度 %d/%d] 成功: %s", currentFinished, totalSizes, fileName));
} else {
failCount.incrementAndGet();
// System.out.println(String.format("[进度 %d/%d] 失败: %s", currentFinished, totalSizes, fileName));
}
}, threadPool);
// 添加任务
futures.add(future);
}
// 阻塞主线程等待所有720个任务全部跑完
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
// 关闭线程池
executor.shutdown();
// System.out.println("总任务数量:" + totalSizes);
// System.out.println("成功抓取:" + successCount.get() + "张");
// System.out.println("失败:" + failCount.get() + "张");
// System.out.println("图片已全部保存在:" + "D:\\work\\temp\\");
}
/**
*
*
* @param userId userId
* @param channelId
* @param time (Java 8 LocalDateTime)
* @param dir ( / \\ "D:/temp/")
* @param fileName ( "pic_120000.jpg")
* @return File File null
*/
private File captureAtTime(int userId, int channelId, LocalDateTime time, String dir, String fileName) {
LocalDateTime stopTime = time.plusSeconds(5);
// 将 Java 的 LocalDateTime 翻译成海康 SDK 认识的底层结构体
HCNetSDK.NET_DVR_TIME startDvrTime = convertToDvrTime(time);
HCNetSDK.NET_DVR_TIME stopDvrTime = convertToDvrTime(stopTime);
String tempVideoPath = dir + "temp_slice_" + UUID.randomUUID() + ".mp4";
String finalImagePath = dir + fileName;
try {
hkNVR.captureHistoryImage(
userId,
channelId,
startDvrTime,
stopDvrTime,
tempVideoPath,
finalImagePath
);
// 抓图流程走完后,检查图片是否真的生成成功了
File resultImage = new File(finalImagePath);
if (resultImage.exists() && resultImage.length() > 0) {
return resultImage;
} else {
return null; // 抓图失败(可能是那个时间点设备断网或无录像)
}
} catch (Exception e) {
return null;
} finally {
File tempFile = new File(tempVideoPath);
if (tempFile.exists()) {
tempFile.delete();
}
}
}
private HCNetSDK.NET_DVR_TIME convertToDvrTime(LocalDateTime currentTime) {
HCNetSDK.NET_DVR_TIME netDvrTime = new HCNetSDK.NET_DVR_TIME();
netDvrTime.dwYear = (short) currentTime.getYear();
netDvrTime.dwMonth = (short) currentTime.getMonthValue();
netDvrTime.dwDay = (short) currentTime.getDayOfMonth();
netDvrTime.dwHour = (short) currentTime.getHour();
netDvrTime.dwMinute = (short) currentTime.getMinute();
netDvrTime.dwSecond = (short) currentTime.getSecond();
return netDvrTime;
}
// 按照时间范围获取录像文件
public List<RecordFile> getRecordFileByTime(Long id, LocalDateTime startTime, LocalDateTime endTime) {
Camera camera = cameraService.getById(id);
List<RecordFile> recordFile = new ArrayList<>();
Integer userId = (Integer) vcrlUserIdContainer.getlUserId(camera.getNvrId().intValue());
try {
recordFile = hkNVR.findRecordFile(userId, camera.getChannelId(), startTime, endTime);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
return recordFile;
}
}

@ -688,7 +688,7 @@ public interface HCNetSDK extends Library {
public static final int NET_DVR_GET_CCDPARAMCFG_EX = 3368;//获取前端参数(扩展)
public static final int NET_DVR_SET_CCDPARAMCFG_EX = 3369;//设置前端参数(扩展)
public static final int NET_DVR_GET_FOCUSMODECFG = 3305;//获取快球聚焦模式信息
public static final int NET_DVR_SET_FOCUSMODECFG = 13306;//设置快球聚焦模式信息
public static final int NET_DVR_SET_FOCUSMODECFG = 3306;//设置快球聚焦模式信息
public static final int NET_DVR_GET_SUPPLEMENTLIGHT = 3728; //获取内置补光灯配置协议
public static final int NET_DVR_SET_SUPPLEMENTLIGHT = 3729; //设置内置补光灯配置协议

@ -15,7 +15,6 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
@ -141,81 +140,6 @@ public class HkNVR extends AbstractNVR {
public String capturePic(int luerId, int iChannel,String fileName) {
return captureP(luerId, iChannel,fileName);
}
@Override
public boolean captureHistoryImage(int luerId, int iChannel,
HCNetSDK.NET_DVR_TIME startDvrTime,
HCNetSDK.NET_DVR_TIME stopDvrTime,
String tempVideoPath,
String finalImagePath) {
// 1. 构建下载条件结构体
HCNetSDK.NET_DVR_PLAYCOND downloadCond = new HCNetSDK.NET_DVR_PLAYCOND();
downloadCond.dwChannel = iChannel;
// 设置开始时间
downloadCond.struStartTime.dwYear = startDvrTime.dwYear;
downloadCond.struStartTime.dwMonth = startDvrTime.dwMonth;
downloadCond.struStartTime.dwDay = startDvrTime.dwDay;
downloadCond.struStartTime.dwHour = startDvrTime.dwHour;
downloadCond.struStartTime.dwMinute = startDvrTime.dwMinute;
downloadCond.struStartTime.dwSecond = startDvrTime.dwSecond;
// 设置结束时间
downloadCond.struStopTime.dwYear = stopDvrTime.dwYear;
downloadCond.struStopTime.dwMonth = stopDvrTime.dwMonth;
downloadCond.struStopTime.dwDay = stopDvrTime.dwDay;
downloadCond.struStopTime.dwHour = stopDvrTime.dwHour;
downloadCond.struStopTime.dwMinute = stopDvrTime.dwMinute;
downloadCond.struStopTime.dwSecond = stopDvrTime.dwSecond;
downloadCond.write();
// 2. 获取下载句柄
int lDownloadHandle = hCNetSDK.NET_DVR_GetFileByTime_V40(luerId, tempVideoPath, downloadCond);
if (lDownloadHandle == -1) {
System.out.println("建立下载通道失败,错误码:" + hCNetSDK.NET_DVR_GetLastError());
return false;
}
// 3. 发送下载控制指令
boolean playControl = hCNetSDK.NET_DVR_PlayBackControl_V40(lDownloadHandle, HCNetSDK.NET_DVR_PLAYSTART, null, 0, null, null);
if (!playControl) {
System.out.println("发送下载控制指令失败,错误码:" + hCNetSDK.NET_DVR_GetLastError());
hCNetSDK.NET_DVR_StopGetFile(lDownloadHandle); //下载用的接口,停止必须用 StopGetFile不能用 StopPlayBack
return false; // 如果指令发送失败,必须 return 终止,不能往下走
}
// 给操作系统 1 秒钟的缓冲时间,确保视频文件彻底写入硬盘并解除占用
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// 7. 调用 ffmpeg 进行抽取图片
ProcessBuilder processBuilder = new ProcessBuilder(
"ffmpeg", "-y", "-err_detect", "ignore_err", "-i", tempVideoPath, "-vframes", "1", "-q:v", "2", "-f", "image2", finalImagePath
);
// 重定向 FFmpeg 的输出流,防止缓冲区撑满导致 Java 进程死锁卡住
processBuilder.redirectErrorStream(true);
try {
Process process = processBuilder.start();
java.io.InputStream inputStream = process.getInputStream();
byte[] buffer = new byte[1024];
while (inputStream.read(buffer) != -1) {
// 静默读取,不打印,清空缓冲区
}
// 等待 FFmpeg 执行完毕
int exitCode = process.waitFor();
if (exitCode == 0) {
return true;
} else {
return false;
}
} catch (IOException | InterruptedException e) {
System.out.println("ffmpeg 执行失败:" + e);
return false;
}
}
/**
*
*

@ -1,14 +0,0 @@
package com.sz.admin.monitor.service;
import com.mybatisflex.core.service.IService;
import com.sz.admin.monitor.pojo.dto.blurDetection.BlurDetectionDTO;
import com.sz.admin.monitor.pojo.po.BlurDetection;
/**
* ClassName: BlurDetectionService
* Package: com.sz.admin.monitor.service
* Description:
*/
public interface BlurDetectionService extends IService<BlurDetection> {
Boolean saveOrUpdateBlur(BlurDetectionDTO blurDetectionDTO);
}

@ -25,7 +25,4 @@ public interface CameraSnapshotService extends IService<CameraSnapshot> {
// 根据摄像头ID查询快照列表
List<CameraSnapshotVO> getExistSnapshotList(Long id);
void deleteByCameraIdAndType(Long cameraId, int i);
CameraSnapshotVO selectByCameraIdAndType(Long cameraId, int i);
}

@ -1,15 +0,0 @@
package com.sz.admin.monitor.service;
import com.mybatisflex.core.service.IService;
import com.sz.admin.monitor.pojo.dto.dataModel.CanvasCfgDTO;
import com.sz.admin.monitor.pojo.po.CanvasCfgDO;
/**
* @description:
* @author xq
* @version 1.0
*/
public interface CanvasCfgService extends IService<CanvasCfgDO> {
void saveOrUpdateCanvas(CanvasCfgDTO canvasCfgDTO);
}

@ -1,17 +0,0 @@
package com.sz.admin.monitor.service;
import com.mybatisflex.core.service.IService;
import com.sz.admin.monitor.pojo.po.CanvasCfgDO;
import com.sz.admin.monitor.pojo.po.ComputerRoom;
import java.util.List;
/**
* ClassName: ComputerRoomService
* Package: com.sz.admin.monitor.service
* Description:
*/
public interface ComputerRoomService extends IService<ComputerRoom> {
// 获取变电站的机房列表
List<ComputerRoom> getComputerRoomList(Long substationId);
}

@ -3,8 +3,6 @@ package com.sz.admin.monitor.service;
import com.mybatisflex.core.service.IService;
import com.sz.admin.monitor.pojo.po.Nvr;
import java.util.List;
/**
* ClassName: DeviceService
* Package: com.sz.admin.monitor.service
@ -15,7 +13,4 @@ public interface DeviceService extends IService<Nvr> {
// 设备登录
Object deviceLogin(Long id);
// 历史视频的算法分析
void captureHistoryAndCompose(List<Long> ids);
}

@ -1,13 +0,0 @@
package com.sz.admin.monitor.service;
import com.mybatisflex.core.service.IService;
import com.sz.admin.monitor.pojo.po.FileStorageDO;
/**
* @author xq
* @description:
* @date 2026/2/2 22:18
*/
public interface FileStorageService extends IService<FileStorageDO> {
}

@ -13,5 +13,5 @@ public interface ForwardService {
/**
*
*/
void enrichAndForward(String url,BoxAlarmReportDto boxAlarm);
void enrichAndForward(BoxAlarmReportDto boxAlarm);
}

@ -1,15 +0,0 @@
package com.sz.admin.monitor.service;
import com.mybatisflex.core.service.IService;
import com.sz.admin.monitor.pojo.dto.dataModel.GridCfgDTO;
import com.sz.admin.monitor.pojo.po.GridCfgDO;
/**
* @author xq
* @description:
* @date 2026/1/29 11:45
*/
public interface GridCfgService extends IService<GridCfgDO> {
void saveOrUpdateGrid(GridCfgDTO gridCfgDTO);
}

@ -3,12 +3,9 @@ package com.sz.admin.monitor.service;
import com.mybatisflex.core.service.IService;
import com.sz.admin.monitor.pojo.dto.edgebox.AlgorithmTaskDTO;
import com.sz.admin.monitor.pojo.po.Camera;
import com.sz.admin.monitor.pojo.po.RecordFile;
import com.sz.admin.monitor.pojo.vo.watchful.WatchfulVO;
import com.sz.admin.monitor.utils.AlgMediaConfigResponse;
import java.util.List;
/**
* ClassName: IpChannelService
* Package: com.sz.admin.monitor.service
@ -38,9 +35,5 @@ public interface IpChannelService extends IService<Camera> {
// 配置算法任务
AlgMediaConfigResponse configAlg(AlgorithmTaskDTO algTaskConfigDto);
void captureHistoryAndCompose(Long id);
List<RecordFile> getVideoByTime(Long id, String startTime, String endTime);
// void resetSentinel(int channelId, int watchTime, int presetIndex);
}

@ -1,18 +0,0 @@
package com.sz.admin.monitor.service;
import com.mybatisflex.core.service.IService;
import com.sz.admin.monitor.pojo.dto.dataModel.ModelContentDTO;
import com.sz.admin.monitor.pojo.po.ModelContentDO;
import java.util.HashMap;
import java.util.List;
/**
* @description:
* @author xq
* @version 1.0
*/
public interface ModelContentService extends IService<ModelContentDO> {
void saveOrUpdateModel(List<ModelContentDTO> list,String menuType);
}

@ -17,7 +17,4 @@ public interface TreeService {
// 获取视频预览的树型结构数据
List<TreeNodeVO> buildTreePreview();
List<TreeNodeVO> buildTreeComputeRoom();
}

@ -1,12 +0,0 @@
package com.sz.admin.monitor.service;
import com.mybatisflex.core.service.IService;
import com.sz.admin.monitor.pojo.dto.dataModel.ModelContentDTO;
import com.sz.admin.monitor.pojo.po.Substation;
import com.sz.admin.monitor.pojo.po.UserModelContentDO;
import java.util.List;
public interface UserModelContentService extends IService<UserModelContentDO> {
public void saveOrUpdateModel(List<ModelContentDTO> list);
}

@ -39,7 +39,6 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Base64;
import java.util.HashMap;
@ -143,22 +142,17 @@ public class AlgorithmTaskServiceImpl extends ServiceImpl<AlgorithmTaskMapper, A
websocketRedisService.sendServiceToWs(msg);
// 统一处理报警信息后,将摄像头信息+报警信息上报给别人服务器
BoxAlarmReportDto reportDto = new BoxAlarmReportDto();
// 1. 获取你的时间字符串
String timeStr = alarmReportDto.getTime();
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime localDateTime = LocalDateTime.parse(timeStr, timeFormatter);
// 转成时间戳(毫秒)
long timestamp = localDateTime.toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
reportDto.setAlarmTime(timestamp);
reportDto.setAlarmTypeDesc(alarmReportEnums.getAlarmDescription());
reportDto.setAlarmType(alarmReportEnums.getAlarmCode());
reportDto.setAlarmReportTime(LocalDateTime.parse(alarmReportDto.getTime(), formatter));
reportDto.setAlarmReportType(alarmReportEnums.getAlarmDescription());
reportDto.setDescription(alarmReportDto.getResult().getDescription());
reportDto.setBaseImage(imageData);
reportDto.setAlarmImage(imageDataLabeled);
reportDto.setCaptureImage(imageDataLabeled);
reportDto.setUrl(algorithmTask.getUrl());
reportDto.setCameraName(camera.getName());
reportDto.setCameraId(cameraId);
reportDto.setCameraNo(camera.getCameraNo());
try {
forwardService.enrichAndForward(algorithmTask.getUrl(),reportDto);
forwardService.enrichAndForward(reportDto);
}catch ( Exception e)
{
log.error("报警信息上报失败", e);

@ -1,65 +0,0 @@
package com.sz.admin.monitor.service.impl;
import cn.hutool.core.util.ObjUtil;
import com.mybatisflex.core.update.UpdateChain;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.sz.admin.monitor.mapper.BlurDetectionMapper;
import com.sz.admin.monitor.mapper.CameraSnapshotMapper;
import com.sz.admin.monitor.pojo.dto.blurDetection.BlurDetectionDTO;
import com.sz.admin.monitor.pojo.po.BlurDetection;
import com.sz.admin.monitor.service.BlurDetectionService;
import com.sz.admin.monitor.service.CameraSnapshotService;
import com.sz.core.common.exception.common.BusinessException;
import com.sz.core.util.BeanCopyUtils;
import com.sz.platform.enums.AdminResponseEnum;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
/**
* ClassName: BlurDetectionServiceImpl
* Package: com.sz.admin.monitor.service.impl
* Description:
*/
@Service
public class BlurDetectionServiceImpl extends ServiceImpl<BlurDetectionMapper, BlurDetection> implements BlurDetectionService {
@Resource
private BlurDetectionMapper blurDetectionMapper;
@Resource
private CameraSnapshotService cameraSnapshotService;
@Override
@Transactional
public Boolean saveOrUpdateBlur(BlurDetectionDTO blurDetectionDTO) {
Long id = blurDetectionDTO.getId();
Integer enable = blurDetectionDTO.getEnable();
if(ObjUtil.isNotNull(id))
{
// 修改
if(enable==0)
{
// 关闭直接将enable状态修改成0
boolean update = UpdateChain.of(BlurDetection.class)
.set(BlurDetection::getEnable, 0)
.where(BlurDetection::getId)
.eq(id)
.update();
}else {
// 删除之前的基准图。抓拍一张最新的基准图保存到数据库
// cameraSnapshotService.deleteByCameraIdAndType(cameraId,4);
// cameraSnapshotService.captureAndSave(cameraId, 4);
// 保存数据库
blurDetectionMapper.update(BeanCopyUtils.copy(blurDetectionDTO, BlurDetection.class));
}
}else {
// 新增
// cameraSnapshotService.captureAndSave(cameraId, 4);
BlurDetection detection = BeanCopyUtils.copy(blurDetectionDTO, BlurDetection.class);
detection.setCreateTime(LocalDateTime.now());
detection.setUpdateTime(LocalDateTime.now());
blurDetectionMapper.insert(detection);
}
return true;
}
}

@ -1,34 +1,23 @@
package com.sz.admin.monitor.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mybatisflex.core.logicdelete.LogicDeleteManager;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.sz.admin.monitor.enums.HKPTZPreEnum;
import com.sz.admin.monitor.listener.mode.CameraDeleteEvent;
import com.sz.admin.monitor.mapper.*;
import com.sz.admin.monitor.pojo.dto.camera.CameraListDTO;
import com.sz.admin.monitor.pojo.dto.camera.CameraUpdateDTO;
import com.sz.admin.monitor.pojo.po.AlgorithmTask;
import com.sz.admin.monitor.pojo.po.Camera;
import com.sz.admin.monitor.pojo.po.EdgeBox;
import com.sz.admin.monitor.pojo.po.Nvr;
import com.sz.admin.monitor.pojo.po.table.CameraTableDef;
import com.sz.admin.monitor.pojo.vo.camera.CameraVO;
import com.sz.admin.monitor.pojo.vo.camerasnapshot.CameraSnapshotVO;
import com.sz.admin.monitor.pojo.vo.edgebox.AlgorithmTaskVO;
import com.sz.admin.monitor.pojo.vo.preset.PresetVO;
import com.sz.admin.monitor.sdk.ManageNVR;
import com.sz.admin.monitor.enums.HCPlayControlEnum;
import com.sz.admin.monitor.service.*;
import com.sz.admin.monitor.service.AlgorithmTaskService;
import com.sz.admin.monitor.service.CameraService;
import com.sz.admin.monitor.utils.AiBoxRequestUtil;
import com.sz.admin.monitor.utils.RtspUtil;
import com.sz.admin.monitor.utils.ZLMediaKitUtils;
import com.sz.admin.teacher.pojo.po.TeacherStatistics;
import com.sz.admin.teacher.pojo.vo.TeacherStatisticsVO;
import com.sz.core.common.entity.PageResult;
import com.sz.core.common.entity.SelectIdsDTO;
import com.sz.core.common.enums.CommonResponseEnum;
@ -38,10 +27,10 @@ import com.sz.core.util.BeanCopyUtils;
import com.sz.core.util.PageUtils;
import com.sz.core.util.Utils;
import com.sz.platform.enums.AdminResponseEnum;
import com.sz.security.core.util.LoginUtils;
import jakarta.annotation.Resource;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -49,7 +38,6 @@ import org.springframework.transaction.annotation.Transactional;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static com.sz.admin.monitor.pojo.po.table.NvrTableDef.NVR;
@ -75,6 +63,8 @@ public class CameraServiceImpl extends ServiceImpl<CameraMapper, Camera> impleme
private AiBoxRequestUtil aiBoxRequestUtil;
@Resource
private EdgeBoxMapper edgeBoxMapper;
@Resource
private ApplicationEventPublisher applicationEventPublisher;
// 自己注入自己
@Resource
@Lazy
@ -136,37 +126,45 @@ public class CameraServiceImpl extends ServiceImpl<CameraMapper, Camera> impleme
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void remove(SelectIdsDTO dto) {
CommonResponseEnum.INVALID_ID.assertTrue(dto.getIds().isEmpty());
List<Camera> cameraList = listByIds(dto.getIds());
if (cameraList.isEmpty()) {
return;
}
List<AlgorithmTaskVO> taskList = algorithmTaskService.listByCameraIds(dto.getIds());
for (AlgorithmTaskVO task : taskList) {
if (task != null && task.getAlgTaskSession() != null) {
try {
Long cameraId = task.getCameraId();
Camera camera = this.getById(cameraId);
Long boxId = camera.getBoxId();
EdgeBox edgeBox = edgeBoxMapper.selectOneById(boxId);
// 删除任务
aiBoxRequestUtil.controlTask(edgeBox.getIp(), edgeBox.getPort(), task.getAlgTaskSession(), 0);
aiBoxRequestUtil.deleteTask(edgeBox.getIp(), edgeBox.getPort(), task.getAlgTaskSession());
} catch (Exception e) {
log.error("清理 AI 盒子任务失败摄像机ID: {}, Session: {}", task.getCameraId(), task.getAlgTaskSession(), e);
throw new BusinessException(AdminResponseEnum.OPERATION_FAIL,null,"同步清理设备算法任务失败,请检查网络后重试");
}
}
}
for (Camera camera : cameraList) {
// 删除通道
Long boxId = camera.getBoxId();
EdgeBox edgeBox = edgeBoxMapper.selectOneById(boxId);
aiBoxRequestUtil.deleteVideoChannel(edgeBox.getIp(), edgeBox.getPort(), camera.getId().toString());
}
cameraService.batchDeleteLocalData(dto.getIds(), taskList);
// 直接删除摄像头
cameraMapper.deleteBatchByIds(dto.getIds());
// 发布事件
applicationEventPublisher.publishEvent(new CameraDeleteEvent((List<Long>) dto.getIds()));
// List<AlgorithmTaskVO> taskList = algorithmTaskService.listByCameraIds(dto.getIds());
// for (AlgorithmTaskVO task : taskList) {
// if (task != null && task.getAlgTaskSession() != null) {
// try {
// Long cameraId = task.getCameraId();
// Camera camera = this.getById(cameraId);
// Long boxId = camera.getBoxId();
// EdgeBox edgeBox = edgeBoxMapper.selectOneById(boxId);
// // 删除任务
// aiBoxRequestUtil.controlTask(edgeBox.getIp(), edgeBox.getPort(), task.getAlgTaskSession(), 0);
// aiBoxRequestUtil.deleteTask(edgeBox.getIp(), edgeBox.getPort(), task.getAlgTaskSession());
// } catch (Exception e) {
// log.error("清理 AI 盒子任务失败摄像机ID: {}, Session: {}", task.getCameraId(), task.getAlgTaskSession(), e);
// throw new BusinessException(AdminResponseEnum.OPERATION_FAIL,null,"同步清理设备算法任务失败,请检查网络后重试");
// }
// }
// }
// for (Camera camera : cameraList) {
// // 删除通道
// Long boxId = camera.getBoxId();
// if (boxId == null)
// continue;
// EdgeBox edgeBox = edgeBoxMapper.selectOneById(boxId);
// aiBoxRequestUtil.deleteVideoChannel(edgeBox.getIp(), edgeBox.getPort(), camera.getId().toString());
// }
// cameraService.batchDeleteLocalData(dto.getIds(), taskList);
}
@Transactional(rollbackFor = Exception.class)
public void batchDeleteLocalData(List<? extends Serializable> cameraIds, List<AlgorithmTaskVO> taskList) {
// 1. 批量删除预置位

@ -7,6 +7,7 @@ import com.sz.admin.monitor.mapper.CameraMapper;
import com.sz.admin.monitor.mapper.CameraSnapshotMapper;
import com.sz.admin.monitor.pojo.po.Camera;
import com.sz.admin.monitor.pojo.po.CameraSnapshot;
import com.sz.admin.monitor.pojo.po.table.CameraSnapshotTableDef;
import com.sz.admin.monitor.pojo.vo.camerasnapshot.CameraSnapshotVO;
import com.sz.admin.monitor.sdk.ManageNVR;
import com.sz.admin.monitor.service.CameraService;
@ -43,6 +44,7 @@ public class CameraSnapshotServiceImpl extends ServiceImpl<CameraSnapshotMapper,
snapshot.setImagePath(path);
snapshot.setCameraId(id);
snapshot.setCheckType(triggerType);
// 先删除,后插入
cameraSnapshotMapper.insert(snapshot);
return BeanCopyUtils.copy(snapshot, CameraSnapshotVO.class);
}
@ -81,25 +83,6 @@ public class CameraSnapshotServiceImpl extends ServiceImpl<CameraSnapshotMapper,
return BeanCopyUtils.copyList(cameraSnapshotMapper.selectListByQuery(wrapper), CameraSnapshotVO.class);
}
@Override
public void deleteByCameraIdAndType(Long cameraId, int i) {
QueryWrapper wrapper = QueryWrapper.create().select().from(CameraSnapshot.class)
.where(CameraSnapshot::getCameraId).eq(cameraId)
.and(CameraSnapshot::getCheckType).eq(i);
CameraSnapshot cameraSnapshot = cameraSnapshotMapper.selectOneByQuery(wrapper);
if (cameraSnapshot != null) {
cameraSnapshotMapper.delete(cameraSnapshot);
}
}
@Override
public CameraSnapshotVO selectByCameraIdAndType(Long cameraId, int i) {
QueryWrapper wrapper = QueryWrapper.create().select().from(CameraSnapshot.class)
.where(CameraSnapshot::getCameraId).eq(cameraId)
.and(CameraSnapshot::getCheckType).eq(i);
CameraSnapshot cameraSnapshot = cameraSnapshotMapper.selectOneByQuery(wrapper);
return BeanCopyUtils.copy(cameraSnapshot, CameraSnapshotVO.class);
}
private void deleteSnapshotByChannelIdAndPresetIndex(Long id, int presetIndex) {

@ -1,51 +0,0 @@
package com.sz.admin.monitor.service.impl;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.sz.admin.monitor.mapper.CanvasCfgMapper;
import com.sz.admin.monitor.pojo.dto.dataModel.CanvasCfgDTO;
import com.sz.admin.monitor.pojo.po.CanvasCfgDO;
import com.sz.admin.monitor.service.CanvasCfgService;
import org.springframework.stereotype.Service;
/**
* @author xq
* @description:
* @date 2026/1/29 11:34
*/
@Service
public class CanvasCfgServiceImpl extends ServiceImpl<CanvasCfgMapper, CanvasCfgDO> implements CanvasCfgService {
@Override
public void saveOrUpdateCanvas(CanvasCfgDTO canvasCfgDTO) {
if(ObjectUtil.isEmpty(canvasCfgDTO))
return;
CanvasCfgDO canvasCfgDO = CanvasCfgDTO.dtoConvertDO(canvasCfgDTO);
if(StrUtil.isEmpty(canvasCfgDO.getUserId())) {
saveOrUpdate(canvasCfgDO);
return;
}
QueryWrapper queryWrapper = QueryWrapper
.create()
.from(CanvasCfgDO.class)
.eq(CanvasCfgDO.USER_ID, canvasCfgDTO.getUserId())
.limit(1);
CanvasCfgDO one = getOne(queryWrapper);
// CanvasCfgDO one = getOne(new QueryWrapper<CanvasCfgDO>()
// .eq(CanvasCfgDO.USER_ID, canvasCfgDTO.getUserId())
// .last("limit 1")
// );
if(ObjectUtil.isEmpty(one)) {
saveOrUpdate(canvasCfgDO);
return;
}
canvasCfgDO.setId(one.getId());
saveOrUpdate(canvasCfgDO);
}
}

@ -1,48 +0,0 @@
package com.sz.admin.monitor.service.impl;
import com.mybatisflex.core.BaseMapper;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.sz.admin.monitor.mapper.ComputeRoomMapper;
import com.sz.admin.monitor.mapper.NvrMapper;
import com.sz.admin.monitor.pojo.po.ComputerRoom;
import com.sz.admin.monitor.pojo.po.ModelContentDO;
import com.sz.admin.monitor.pojo.po.Nvr;
import com.sz.admin.monitor.pojo.po.table.ComputerRoomTableDef;
import com.sz.admin.monitor.pojo.po.table.ModelContentDOTableDef;
import com.sz.admin.monitor.pojo.po.table.NvrTableDef;
import com.sz.admin.monitor.service.ComputerRoomService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* ClassName: ComputerRoomServiceImpl
* Package: com.sz.admin.monitor.service.impl
* Description:
*/
@Service
public class ComputerRoomServiceImpl extends ServiceImpl<ComputeRoomMapper, ComputerRoom> implements ComputerRoomService {
@Resource
private ComputeRoomMapper computerRoomMapper;
@Override
public List<ComputerRoom> getComputerRoomList(Long substationId) {
// 构建 EXISTS 子查询
QueryWrapper queryWrapper = QueryWrapper.create()
.select(
ComputerRoomTableDef.COMPUTER_ROOM.ID,
ComputerRoomTableDef.COMPUTER_ROOM.SUBSTATION_ID,
ComputerRoomTableDef.COMPUTER_ROOM.NAME,
ComputerRoomTableDef.COMPUTER_ROOM.DESCRIPTION
)
.from(ComputerRoomTableDef.COMPUTER_ROOM)
.where(ComputerRoomTableDef.COMPUTER_ROOM.SUBSTATION_ID.eq(substationId))
.and(ComputerRoomTableDef.COMPUTER_ROOM.ID.in(
QueryWrapper.create()
.select(ModelContentDOTableDef.MODEL_CONTENT_DO.ROOM_ID)
.from(ModelContentDOTableDef.MODEL_CONTENT_DO)
));
return computerRoomMapper.selectListByQuery(queryWrapper);
}
}

@ -1,195 +0,0 @@
package com.sz.admin.monitor.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.mybatisflex.core.query.QueryWrapper;
import com.sz.admin.monitor.pojo.dto.dataModel.CanvasCfgDTO;
import com.sz.admin.monitor.pojo.dto.dataModel.DataModelDTO;
import com.sz.admin.monitor.pojo.dto.dataModel.GridCfgDTO;
import com.sz.admin.monitor.pojo.dto.dataModel.ModelContentDTO;
import com.sz.admin.monitor.pojo.po.CanvasCfgDO;
import com.sz.admin.monitor.pojo.po.GridCfgDO;
import com.sz.admin.monitor.pojo.po.ModelContentDO;
import com.sz.admin.monitor.pojo.po.UserModelContentDO;
import com.sz.admin.monitor.pojo.que.dataModel.DataModelQue;
import com.sz.admin.monitor.service.CanvasCfgService;
import com.sz.admin.monitor.service.GridCfgService;
import com.sz.admin.monitor.service.ModelContentService;
import com.sz.admin.monitor.service.UserModelContentService;
import com.sz.core.common.entity.ApiResult;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author xq
* @description:
* @date 2026/1/29 10:51
*/
@Service
public class DataModelServiceImpl {
@Autowired
private CanvasCfgService canvasCfgService; // 画板
@Autowired
private GridCfgService gridCfgService; // 网格
@Autowired
private ModelContentService modelContentService; //模型
@Autowired
private UserModelContentService userModelContentService; //用户模型中间表
private static final String userId = "1";
public ApiResult<DataModelDTO> getModelData(DataModelQue que) {
if (StrUtil.isEmpty(que.getUserId()))
que.setUserId(userId);
QueryWrapper queryWrapper = QueryWrapper.create()
.from(CanvasCfgDO.class)
.eq(CanvasCfgDO.USER_ID, que.getUserId())
.limit(1);
// 获取画板数据
CanvasCfgDO canvasCfgDO = canvasCfgService.getOne(queryWrapper);
if (ObjectUtil.isEmpty(canvasCfgDO)) {
canvasCfgDO = initCanvasCfg();
canvasCfgService.saveOrUpdate(canvasCfgDO);
}
// 获取网格数据
GridCfgDO gridCfgDO = gridCfgService.getOne(QueryWrapper.create()
.from(GridCfgDO.class)
.eq(GridCfgDO.USER_ID, que.getUserId())
.limit(1)
);
if (ObjectUtil.isEmpty(gridCfgDO)) {
gridCfgDO = initGridCfg();
gridCfgService.saveOrUpdate(gridCfgDO);
}
//中间表
List<UserModelContentDO> userModelList = userModelContentService.list(QueryWrapper.create()
.from(UserModelContentDO.class)
.eq(UserModelContentDO.USER_ID, userId)
.eq(UserModelContentDO.MENU_TYPE, que.getMenuType())
);
ArrayList<ModelContentDTO> objects = new ArrayList<>();
if (CollectionUtil.isEmpty(userModelList)) {
DataModelDTO build = DataModelDTO.builder()
.canvasCfg(CanvasCfgDO.doConvertDTO(canvasCfgDO))
.gridCfg(GridCfgDO.doConvertDTO(gridCfgDO))
.json(objects)
.build();
return ApiResult.success(build);
}
// List<String> collect = userModelList.stream()
// .map(item -> item.getModelContentId())
// .collect(Collectors.toList());
List<ModelContentDO> modelContentDOS = modelContentService.list(QueryWrapper.create()
.from(ModelContentDO.class)
.eq(ModelContentDO::getRoomId, que.getRoomId())
// .in(ModelContentDO.ID, collect)
.orderBy(ModelContentDO.MODEL_INDEX, true)
);
for (ModelContentDO modelContentDO : modelContentDOS) {
objects.add(ModelContentDO.doConvertDTO(modelContentDO));
}
DataModelDTO build = DataModelDTO.builder()
.canvasCfg(CanvasCfgDO.doConvertDTO(canvasCfgDO))
.gridCfg(GridCfgDO.doConvertDTO(gridCfgDO))
.json(objects)
.build();
return ApiResult.success(build);
}
/**
*
*
* @return
*/
public GridCfgDO initGridCfg() {
return GridCfgDO.builder()
.userId(userId)
.enabled(true)
.align(true)
.size(10)
.build();
}
/**
*
*
* @return
*/
public CanvasCfgDO initCanvasCfg() {
return CanvasCfgDO.builder()
.width(1920)
.userId(userId)
.height(1080)
.scale(1D)
.color("")
.img("")
.guide(true)
.adsorp(true)
.adsorpDiff(5D)
.transformOrigin("{\"x\":0,\"y\":0}")
.dragOffset("{\"x\":0,\"y\":0}")
.build();
}
/**
*
*
* @param dataModelDTO
* @param request
* @param response
* @return
*/
@Transactional
public ApiResult saveOrUpdateModelData(DataModelDTO dataModelDTO, HttpServletRequest request, HttpServletResponse response) {
if (ObjectUtil.isEmpty(dataModelDTO))
throw new IllegalArgumentException("传值错误");
CanvasCfgDTO canvasCfg = dataModelDTO.getCanvasCfg();
GridCfgDTO gridCfg = dataModelDTO.getGridCfg();
List<ModelContentDTO> json=new ArrayList<>();
for (int i = 0; i < dataModelDTO.getJson().size(); i++) {
ModelContentDTO modelContentDTO = dataModelDTO.getJson().get(i);
modelContentDTO.setModelIndex(i);
modelContentDTO.setSubstationId(dataModelDTO.getSubstationId());
modelContentDTO.setRoomId(dataModelDTO.getRoomId());
json.add(modelContentDTO);
}
//保存、更新面板
canvasCfg.setUserId(userId);
canvasCfgService.saveOrUpdateCanvas(canvasCfg);
//保存、更新网格
gridCfg.setUserId(userId);
gridCfgService.saveOrUpdateGrid(gridCfg);
//保存数据模型
modelContentService.saveOrUpdateModel(json, dataModelDTO.getMenuType());
return ApiResult.success();
}
public List<UserModelContentDO> getList() {
return null;
}
}

@ -8,7 +8,6 @@ import com.sz.admin.monitor.pojo.po.Camera;
import com.sz.admin.monitor.pojo.po.Nvr;
import com.sz.admin.monitor.sdk.ManageNVR;
import com.sz.admin.monitor.service.DeviceService;
import com.sz.admin.monitor.service.IpChannelService;
import com.sz.admin.monitor.utils.AiBoxRequestUtil;
import com.sz.admin.monitor.utils.AlgMediaConfigResponse;
import com.sz.admin.monitor.utils.RtspUtil;
@ -31,10 +30,6 @@ import java.io.UnsupportedEncodingException;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;
/**
@ -54,8 +49,11 @@ public class DeviceServiceImpl extends ServiceImpl<NvrMapper, Nvr> implements De
@Resource
private NvrMapper nvrMapper;
@Resource
private IpChannelService ipChannelService;
private SocketService socketService;
@Resource
private WebsocketRedisService websocketRedisService;
@Resource
private AiBoxRequestUtil aiBoxRequestUtil;
@Override
public Object deviceLogin(Long id) {
@ -70,37 +68,6 @@ public class DeviceServiceImpl extends ServiceImpl<NvrMapper, Nvr> implements De
return result;
}
@Override
public void captureHistoryAndCompose(List<Long> ids) {
if(ids==null || ids.isEmpty())
{
throw new BusinessException(AdminResponseEnum.OPERATION_FAIL,null,"数据为空");
}
ExecutorService threadPool = Executors.newFixedThreadPool(1);
CompletableFuture.runAsync(()->{
try {
List<Nvr> nvrs = nvrMapper.selectListByIds(ids);
// 对每个NVR进行分析
for (Nvr nvr : nvrs) {
// 查询出所有的通道
List<Camera> cameraList = cameraMapper.selectByNvrId(nvr.getId());
// 遍历所有的通道,进行分析
for (Camera camera : cameraList) {
try {
ipChannelService.captureHistoryAndCompose(camera.getId());
}catch (Exception e)
{
log.error("处理camera异常NVRID:{},CameraID:{}",nvr.getId(),camera.getId());
}
}
}
}catch (Exception e)
{
log.error("执行批量分析任务发生全局异常", e);
}
},threadPool);
}
/**
*
* @param id NVRid

@ -1,274 +0,0 @@
package com.sz.admin.monitor.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.sz.admin.monitor.pojo.dto.camera.CameraListDTO;
import com.sz.admin.monitor.pojo.dto.file.FileEntityDTO;
import com.sz.admin.monitor.pojo.dto.file.FileStorageDTO;
import com.sz.admin.monitor.pojo.po.Camera;
import com.sz.admin.monitor.pojo.po.FileStorageDO;
import com.sz.admin.monitor.pojo.po.Nvr;
import com.sz.admin.monitor.pojo.que.file.FileStorageQue;
import com.sz.admin.monitor.pojo.vo.camera.CameraVO;
import com.sz.admin.monitor.service.FileStorageService;
import com.sz.core.common.entity.ApiResult;
import com.sz.core.common.entity.PageResult;
import com.sz.core.datascope.SimpleDataScopeHelper;
import com.sz.core.util.PageUtils;
import com.sz.core.util.Utils;
import com.sz.platform.enums.AdminResponseEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.*;
/**
* @author xq
* @description:
* @date 2026/3/13 15:28
*/
@Service
public class FileServiceImpl {
@Autowired
private FileStorageService fileStorageService;
@Value("${file.object.storage.targetDir:/defaultFile}")
private String targetDir;
// 获取当前操作系统的文件分隔符
private static String separator = "/";
//获取文件流路径(请求路径)
private static String path = "/fileStorage/getPreviewFile/";
/**
*
*
* @param id
* @return
*/
public ApiResult<String> deleteFileById(String id) {
if (ObjectUtil.isEmpty(id))
return ApiResult.error(AdminResponseEnum.OPERATION_FAIL);
FileStorageDO fileStorageDO = fileStorageService.getById(id);
if (ObjectUtil.isEmpty(fileStorageDO))
return ApiResult.error(AdminResponseEnum.OPERATION_FAIL);
fileStorageService.removeById(id);
return ApiResult.success("删除成功");
}
/**
*
*
* @param dto
* @return
*/
public ApiResult<String> editFileNameById(FileEntityDTO dto) {
if (ObjectUtil.isEmpty(dto.getId()))
return ApiResult.error(AdminResponseEnum.OPERATION_FAIL);
FileStorageDO fileStorageDO = fileStorageService.getById(dto.getId());
if (ObjectUtil.isEmpty(fileStorageDO))
return ApiResult.error(AdminResponseEnum.OPERATION_FAIL);
fileStorageDO.setFileName(dto.getFileName());
fileStorageService.saveOrUpdate(fileStorageDO);
return ApiResult.success("修改成功");
}
/**
*
*
* @param que
* @return
*/
public ApiResult<PageResult<FileEntityDTO>> getFileObjectList(FileStorageQue que) {
SimpleDataScopeHelper.start(FileStorageDO.class); // 指定要追加条件的表PO实体
QueryWrapper queryWrapper = QueryWrapper.create()
.from(FileStorageDO.class)
.eq(FileStorageDO.ID, que.getId(), ObjectUtil.isNotEmpty(que.getId()))
.like(FileStorageDO.FILE_NAME, que.getFileName(), ObjectUtil.isNotEmpty(que.getFileName()));
if (ObjectUtil.isNotEmpty(que.getStartTime()) && ObjectUtil.isNotEmpty(que.getEndTime())) {
queryWrapper.between(
FileStorageDO.ADD_TIME,
new Date(que.getStartTime()),
new Date(que.getEndTime())
);
}
queryWrapper.orderBy(FileStorageDO.ADD_TIME, false);
Page<FileStorageDO> page = fileStorageService.pageAs(PageUtils.getPage(que), queryWrapper, FileStorageDO.class);// 调试sql
if (ObjectUtil.isEmpty(page) || page.getRecords().isEmpty())
return ApiResult.success(new PageResult<>(que.getPage().longValue(), que.getLimit().longValue(), 0, 0, new ArrayList<>()));
List<FileEntityDTO> fileEntityDTOS = new ArrayList<>();
for (FileStorageDO record : page.getRecords()) {
FileEntityDTO fileEntityDTO = BeanUtil.copyProperties(record, FileEntityDTO.class);
fileEntityDTO.setFilePath(path + record.getId());
fileEntityDTOS.add(fileEntityDTO);
}
return ApiResult.success(new PageResult<>(
page.getPageNumber(),
page.getPageSize(),
page.getTotalPage(),
page.getTotalRow(),
fileEntityDTOS));
}
// public ApiResult<PageResult<FileEntityDTO>> getFileObjectList(FileStorageQue que) {
// Page<FileStorageDO> doPage = BaseQue.init(que, FileStorageDO.class);
// Page<FileStorageDO> page = fileStorageService.page(doPage, new QueryWrapper<FileStorageDO>()
// .eq(ObjectUtil.isNotEmpty(que.getId()), FileStorageDO.ID, que.getId())
// .like(ObjectUtil.isNotEmpty(que.getFileName()), FileStorageDO.FILE_NAME, que.getFileName())
// .between(
// ObjectUtil.isNotEmpty(que.getStartTime()) && ObjectUtil.isNotEmpty(que.getEndTime())
// , FileStorageDO.ADD_TIME
// , que.getStartTime(), que.getEndTime())
// .orderByDesc(FileStorageDO.ADD_TIME)
// );
// if(ObjectUtil.isEmpty(page) || page.getRecords().isEmpty())
// return ResponseBody.ok();
//
// BasePage<FileEntityDTO> basePage = new BasePage();
// basePage.setTotal(page.getTotal());
// basePage.setSize(page.getSize());
// basePage.setCurrent(page.getCurrent());
// basePage.setPages(page.getPages());
//
// List<FileEntityDTO> fileEntityDTOS = new ArrayList<>();
// for (FileStorageDO record : page.getRecords()) {
// FileEntityDTO fileEntityDTO = BeanUtil.copyProperties(record, FileEntityDTO.class);
// fileEntityDTO.setFilePath(path+record.getId());
// fileEntityDTOS.add(fileEntityDTO);
// }
// basePage.setList(fileEntityDTOS);
// return ApiResult.success(basePage);
// }
/**
*
*
* @param id
* @return
*/
public ResponseEntity<byte[]> getPreviewFile(String id) {
FileStorageDO fileStorageDO = fileStorageService.getById(id);
String filePath = fileStorageDO.getFileUrl();
// 使用Hutool读取文件并转换为Base64编码
byte[] imageBytes = FileUtil.readBytes(filePath);
String mimeType = getMimeType(filePath);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType(mimeType));
headers.setContentLength(imageBytes.length);
return new ResponseEntity<>(imageBytes, headers, HttpStatus.OK);
}
/**
*
*
* @param id
* @return
*/
public ApiResult<FileStorageDTO> getFile(String id) {
FileStorageDO fileStorageDO = fileStorageService.getById(id);
if (ObjectUtil.isEmpty(fileStorageDO))
return ApiResult.error(AdminResponseEnum.OPERATION_FAIL);
String filePath = fileStorageDO.getFileUrl();
// 使用Hutool读取文件并转换为Base64编码
byte[] fileBytes = FileUtil.readBytes(filePath);
String base64Data = Base64.encode(fileBytes);
String mimeType = getMimeType(filePath);
String dataUrl = "data:" + mimeType + ";base64," + base64Data;
FileStorageDTO fileStorageDTO = BeanUtil.copyProperties(fileStorageDO, FileStorageDTO.class);
fileStorageDTO.setBase64Data(dataUrl);
return ApiResult.success(fileStorageDTO);
}
/**
*
*
* @param file
* @return
*/
public ApiResult<FileStorageDTO> saveFile(MultipartFile file) {
Map<String, Object> result = new HashMap<>();
if (file.isEmpty())
return ApiResult.error(AdminResponseEnum.OPERATION_FAIL);
String path = targetDir;
try {
FileUtil.mkdir(path);
// 生成唯一文件名
String fileName = System.currentTimeMillis() + "_" + file.getOriginalFilename();
String filePath = path + separator + fileName;
// 使用Hutool保存文件
FileUtil.writeBytes(file.getBytes(), filePath);
String mimeType = getMimeType(filePath);
FileStorageDTO fileDTO = FileStorageDTO.builder()
.fileUrl(filePath)
.fileSize(ObjectUtil.isNotEmpty(file.getSize()) ? String.valueOf(file.getSize()) : "0")
.fileName(file.getOriginalFilename())
.fileSuffix(mimeType)
.addTime(new Date())
.build();
FileStorageDO fileStorageDO = FileStorageDTO.dtoConvertDO(fileDTO);
fileStorageService.saveOrUpdate(fileStorageDO);
fileDTO.setId(fileStorageDO.getId());
String base64Data = Base64.encode(file.getBytes());
String dataUrl = "data:" + mimeType + ";base64," + base64Data;
fileDTO.setBase64Data(dataUrl);
return ApiResult.success(fileDTO);
} catch (IOException e) {
result.put("success", false);
result.put("message", "文件上传失败: " + e.getMessage());
return ApiResult.error(null);
}
}
public void test01() {
fileStorageService.list().forEach(System.out::println);
}
/**
* MIME
*/
private String getMimeType(String filePath) {
String lowerPath = filePath.toLowerCase();
if (lowerPath.endsWith(".jpg") || lowerPath.endsWith(".jpeg")) {
return "image/jpeg";
} else if (lowerPath.endsWith(".png")) {
return "image/png";
} else if (lowerPath.endsWith(".gif")) {
return "image/gif";
} else if (lowerPath.endsWith(".bmp")) {
return "image/bmp";
} else {
return "image/jpeg"; // 默认JPEG类型
}
}
}

@ -1,17 +0,0 @@
package com.sz.admin.monitor.service.impl;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.sz.admin.monitor.mapper.FileStorageMapper;
import com.sz.admin.monitor.pojo.po.FileStorageDO;
import com.sz.admin.monitor.service.FileStorageService;
import org.springframework.stereotype.Service;
/**
* @author xq
* @description:
* @date 2026/2/2 22:18
*/
@Service
public class FileStorageServiceImpl extends ServiceImpl<FileStorageMapper, FileStorageDO> implements FileStorageService {
}

@ -19,7 +19,7 @@ public class ForwardServiceImpl implements ForwardService {
@Resource
private RestTemplate restTemplate;
@Override
public void enrichAndForward(String url,BoxAlarmReportDto boxAlarm) {
ResponseEntity<String> response = restTemplate.postForEntity(url, boxAlarm, String.class);
public void enrichAndForward(BoxAlarmReportDto boxAlarm) {
ResponseEntity<String> response = restTemplate.postForEntity(boxAlarm.getUrl(), boxAlarm, String.class);
}
}

@ -1,49 +0,0 @@
package com.sz.admin.monitor.service.impl;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.sz.admin.monitor.mapper.GridCfgMapper;
import com.sz.admin.monitor.pojo.dto.dataModel.GridCfgDTO;
import com.sz.admin.monitor.pojo.po.GridCfgDO;
import com.sz.admin.monitor.service.GridCfgService;
import org.springframework.stereotype.Service;
/**
* @author xq
* @description:
* @date 2026/1/29 11:45
*/
@Service
public class GridCfgServiceImpl extends ServiceImpl<GridCfgMapper, GridCfgDO> implements GridCfgService {
@Override
public void saveOrUpdateGrid(GridCfgDTO dto) {
if (ObjectUtil.isEmpty(dto))
return;
GridCfgDO gridCfgDO = GridCfgDTO.dtoConvertDO(dto);
if (StrUtil.isEmpty(gridCfgDO.getUserId())) {
saveOrUpdate(gridCfgDO);
return;
}
QueryWrapper queryWrapper = QueryWrapper.create()
.from(GridCfgDO.class)
.eq(GridCfgDO.USER_ID, gridCfgDO.getUserId())
.limit(1);
GridCfgDO one = getOne(queryWrapper);
// GridCfgDO one = getOne(new QueryWrapper<GridCfgDO>()
// .eq(GridCfgDO.USER_ID, gridCfgDO.getUserId())
// .last("limit 1")
// );
if (ObjectUtil.isEmpty(one)) {
saveOrUpdate(gridCfgDO);
return;
}
gridCfgDO.setId(one.getId());
saveOrUpdate(gridCfgDO);
}
}

@ -1,6 +1,5 @@
package com.sz.admin.monitor.service.impl;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.UUID;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
@ -9,10 +8,16 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.sz.admin.monitor.enums.HCPlayControlEnum;
import com.sz.admin.monitor.mapper.*;
import com.sz.admin.monitor.mapper.AlgorithmTaskMapper;
import com.sz.admin.monitor.mapper.CameraMapper;
import com.sz.admin.monitor.mapper.EdgeBoxMapper;
import com.sz.admin.monitor.mapper.NvrMapper;
import com.sz.admin.monitor.pojo.dto.Ipchannel.ControlDTO;
import com.sz.admin.monitor.pojo.dto.edgebox.AlgorithmTaskDTO;
import com.sz.admin.monitor.pojo.po.*;
import com.sz.admin.monitor.pojo.po.AlgorithmTask;
import com.sz.admin.monitor.pojo.po.Camera;
import com.sz.admin.monitor.pojo.po.EdgeBox;
import com.sz.admin.monitor.pojo.po.Nvr;
import com.sz.admin.monitor.pojo.vo.camerasnapshot.CameraSnapshotVO;
import com.sz.admin.monitor.pojo.vo.ptz.PTZVO;
import com.sz.admin.monitor.pojo.vo.watchful.WatchfulVO;
@ -20,7 +25,10 @@ import com.sz.admin.monitor.sdk.ManageNVR;
import com.sz.admin.monitor.service.CameraSnapshotService;
import com.sz.admin.monitor.service.IpChannelService;
import com.sz.admin.monitor.service.PresetService;
import com.sz.admin.monitor.utils.*;
import com.sz.admin.monitor.utils.AiBoxRequestUtil;
import com.sz.admin.monitor.utils.AlgMediaConfigResponse;
import com.sz.admin.monitor.utils.RtspUtil;
import com.sz.admin.monitor.utils.ZLMediaKitUtils;
import com.sz.core.common.entity.TransferMessage;
import com.sz.core.common.exception.common.BusinessException;
import com.sz.core.util.BeanCopyUtils;
@ -34,9 +42,7 @@ import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.File;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
@ -61,7 +67,7 @@ public class IpChannelServiceImpl extends ServiceImpl<CameraMapper, Camera> impl
private AlgorithmTaskMapper algorithmTaskMapper;
@Resource
private CameraAlarmMapper cameraAlarmMapper;
private PresetService presetService;
@Resource
private AiBoxRequestUtil aiBoxRequestUtil;
@ -122,10 +128,6 @@ public class IpChannelServiceImpl extends ServiceImpl<CameraMapper, Camera> impl
// 进行拉流代理
JSONObject jsonObject =zlMediaKitUtils.addStreamProxy(String.valueOf(id), mainRtsp);
// JSONObject jsonObject = zlMediaKitUtils.addStreamProxy("test", "rtsp://132.239.12.145:554/axis-media/media.amp");
log.info("进行拉流代理的结果:{}", jsonObject.toString());
if ("-1".equals(jsonObject.get("code").toString())) {
throw new BusinessException(AdminResponseEnum.DEVICE_ADD_PROXY_FAIL, null, "服务失败");
}
if ("0".equals(jsonObject.get("code").toString())) {
//拉流代理成功
return zlMediaKitUtils.getPlayUrl(String.valueOf(id));
@ -226,7 +228,7 @@ public class IpChannelServiceImpl extends ServiceImpl<CameraMapper, Camera> impl
}
// 开启和关闭云台守望
// 开启和关闭云台守望 0表示关闭1表示开启
@Override
@Transactional
public boolean ControlSentinel(Long id, int openOrNo, int watchTime, Integer presetIndex) {
@ -235,15 +237,19 @@ public class IpChannelServiceImpl extends ServiceImpl<CameraMapper, Camera> impl
{
flag = manageNVR.watchful(id.intValue(), openOrNo, watchTime, -1);
}else {
if(presetIndex==null)
{
throw new BusinessException(AdminResponseEnum.OPERATION_FAIL, null, "预置点不能为空");
}
flag = manageNVR.watchful(id.intValue(), openOrNo, watchTime, presetIndex);
}
if (flag) {
// 获取当前预置点的ptz坐标并且保存
PTZVO ptzLocation = manageNVR.getPTZLocation(id);
ptzLocation.setCameraId(id);
// PTZVO ptzLocation = manageNVR.getPTZLocation(id);
// ptzLocation.setCameraId(id);
// 将ptz保存到redis中
stringRedisTemplate.opsForValue().set("camera:sentinel", JSON.toJSONString(ptzLocation));
log.info("ptz 坐标:{}", ptzLocation);
// stringRedisTemplate.opsForValue().set("camera:sentinel", JSON.toJSONString(ptzLocation));
// log.info("ptz 坐标:{}", ptzLocation);
Camera camera = this.getById(id);
// 保存守望信息
camera.setWatchfulTime(watchTime);
@ -372,98 +378,6 @@ public class IpChannelServiceImpl extends ServiceImpl<CameraMapper, Camera> impl
}
return response;
}
@Override
public void captureHistoryAndCompose(Long id) {
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss");
// 生成本次任务的专属批次号
String batchId=LocalDateTime.now().format(fmt);
// 拼接出本次任务的专属目录
String baseDir="D:/work/temp/";
String taskDir=baseDir+"task_"+batchId+"/";
File taskDirect = new File(taskDir);
if (!taskDirect.exists()) {
boolean created = taskDirect.mkdirs();
if (!created) {
log.error("创建专属工作目录失败,请检查 D 盘权限!路径: {}", taskDir);
return;
}
}
log.info("保存文件的路径:{}",taskDir);
manageNVR.captureHistoryImage(taskDir,id);
startSequentialComparison(taskDir,id);
}
// 执行相邻图片的滑动窗口对比任务
private void startSequentialComparison(String imageDirPath,Long id) {
// 图片存储目录文件夹
File dir = new File(imageDirPath);
Camera camera = this.getById(id);
Nvr nvr = nvrMapper.selectOneById(camera.getNvrId());
// 获取该目录下所有的.jpg图片
File[] imageFiles = dir.listFiles((d, name) -> name.toLowerCase().endsWith(".jpg") && name.contains("history_"+camera.getId()));
if (imageFiles == null || imageFiles.length < 2) {
log.error("图片数据不足,无法对比");
return;
}
// 将图片进行排序
Arrays.sort(imageFiles, Comparator.comparing(File::getName));
List<CameraAlarm> cameraAlarmList=new ArrayList<>();
for (int i = 0; i < imageFiles.length - 1; i++) {
File currentImg = imageFiles[i];
File nextImg = imageFiles[i + 1];
// System.out.println("正在对比:" + currentImg.getName() + "VS " + nextImg.getName());
// 调用算法进行对比
// 1. 移位对比
Map<String, Object> map = RobustImageMatcherUtil.calculateOffset(currentImg.getPath(), nextImg.getPath());
// 如果出现差异化,直接生成一条记录,保存到报警记录表中
int value = (int) map.get("value");
if (value == 1) {
CameraAlarm cameraAlarm = new CameraAlarm();
cameraAlarm.setCameraId(camera.getId());
cameraAlarm.setCameraName(camera.getName());
cameraAlarm.setCameraNo(camera.getCameraNo());
cameraAlarm.setChannelId(camera.getChannelId());
cameraAlarm.setAlarmAreaId(nvr.getStationId());
cameraAlarm.setAlarmType(1);
cameraAlarm.setStatus(0);
cameraAlarm.setBaseImage(currentImg.getPath());
cameraAlarm.setCaptureImage(nextImg.getPath());
cameraAlarm.setAlgoResult((String) map.get("description"));
cameraAlarmList.add(cameraAlarm);
}
// 2. 模糊对比
BlurDetectorV4.DetectResult detectResult = BlurDetectorV4.doubleDetectBlur(currentImg.getPath(), nextImg.getPath());
if ("1".equals(detectResult.value)){
CameraAlarm cameraAlarm = new CameraAlarm();
cameraAlarm.setCameraId(camera.getId());
cameraAlarm.setCameraName(camera.getName());
cameraAlarm.setCameraNo(camera.getCameraNo());
cameraAlarm.setChannelId(camera.getChannelId());
cameraAlarm.setAlarmAreaId(nvr.getStationId());
cameraAlarm.setAlarmType(6);
cameraAlarm.setStatus(0);
cameraAlarm.setBaseImage(currentImg.getPath());
cameraAlarm.setCaptureImage(nextImg.getPath());
cameraAlarm.setAlgoResult((String) map.get("description"));
cameraAlarmList.add(cameraAlarm);
}
}
// 保存到数据库
cameraAlarmMapper.insertBatch(cameraAlarmList);
}
@Override
public List<RecordFile> getVideoByTime(Long id, String startTime, String endTime) {
List<RecordFile> recordFiles = new ArrayList<>();
try {
recordFiles = manageNVR.getRecordFileByTime(id, LocalDateTime.parse(startTime, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")), LocalDateTime.parse(endTime, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
} catch (Exception e) {
throw new RuntimeException("获取视频失败", e);
}
return recordFiles;
}
// 在这里接收透传过来的 websocket信息进行业务处理
@Override
public void handlerMsg(TransferMessage transferMessage) {

@ -1,76 +0,0 @@
package com.sz.admin.monitor.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.sz.admin.monitor.mapper.ModelContentMapper;
import com.sz.admin.monitor.pojo.dto.dataModel.ModelContentDTO;
import com.sz.admin.monitor.pojo.po.ModelContentDO;
import com.sz.admin.monitor.pojo.po.UserModelContentDO;
import com.sz.admin.monitor.service.ModelContentService;
import com.sz.admin.monitor.service.UserModelContentService;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @author xq
* @description:
* @date 2026/1/29 11:30
*/
@Component
public class ModelContentServiceImpl extends ServiceImpl<ModelContentMapper, ModelContentDO> implements ModelContentService {
@Autowired
private UserModelContentService userModelContentService;
@Resource
private ModelContentMapper modelContentMapper;
/**
*
*
* @param list
* @param menuType
* @return map key addIdModelsid updateIdModelsid
*/
@Override
public void saveOrUpdateModel(List<ModelContentDTO> list, String menuType) {
if (CollectionUtil.isEmpty(list))
return;
// 获取所有roomIds
Set<Long> roomIds = list.stream().map(ModelContentDTO::getRoomId).collect(Collectors.toSet());
// DELETE FROM ry_model_content WHERE room_id in '1'
if (!roomIds.isEmpty())
{
modelContentMapper.deleteByRoomIds(roomIds);
}
for (ModelContentDTO modelContentDTO : list) {
ModelContentDO modelContentDO = ModelContentDTO.dtoConvertDO(modelContentDTO);
disposeDO(modelContentDO);
save(modelContentDO);
}
}
/**
* DOid
*
* @param modelContentDO
*/
public void disposeDO(ModelContentDO modelContentDO) {
//这里是空肯定是add
if (StrUtil.isEmpty(modelContentDO.getId()))
return;
//判断是否是前端生成的id
if (!modelContentDO.getId().matches("\\d+"))
modelContentDO.setId(null);
}
}

@ -1,6 +1,7 @@
package com.sz.admin.monitor.service.impl;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.update.UpdateChain;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.sz.admin.monitor.mapper.CameraMapper;
import com.sz.admin.monitor.mapper.PresetMapper;
@ -84,13 +85,12 @@ public class PresetServiceImpl extends ServiceImpl<PresetMapper, Preset> impleme
throw new BusinessException(AdminResponseEnum.CHANNEL_PRESET_FAIL, null, "当前预置位被设置为守望位,请先关闭或者修改守望位");
}
if (watchful.getWatchfulState() == 0 && isCurrentWatchful) {
Camera updateEntity = new Camera();
updateEntity.setWatchfulIndex(null);
QueryWrapper updateWrapper = QueryWrapper.create()
.from(CameraTableDef.CAMERA)
// update Camera set watchfulIndex = null where id = 132 and watchfulIndex = 1
UpdateChain.of(Camera.class)
.set(CameraTableDef.CAMERA.WATCHFUL_INDEX, null)
.where(CameraTableDef.CAMERA.ID.eq(dto.getCameraId()))
.and(CameraTableDef.CAMERA.WATCHFUL_INDEX.eq(dto.getCurrentPresetIndex()));
int updateCount = cameraMapper.updateByQuery(updateEntity, updateWrapper);
.and(CameraTableDef.CAMERA.WATCHFUL_INDEX.eq(dto.getCurrentPresetIndex()))
.update();
}
}
// 3. 删除预置位表中的数据

@ -1,11 +1,16 @@
package com.sz.admin.monitor.service.impl;
import com.mybatisflex.core.query.QueryWrapper;
import com.sz.admin.monitor.mapper.*;
import com.sz.admin.monitor.mapper.CameraMapper;
import com.sz.admin.monitor.mapper.NvrMapper;
import com.sz.admin.monitor.mapper.RegionMapper;
import com.sz.admin.monitor.mapper.SubstationMapper;
import com.sz.admin.monitor.pojo.po.*;
import com.sz.admin.monitor.pojo.po.Camera;
import com.sz.admin.monitor.pojo.po.Nvr;
import com.sz.admin.monitor.pojo.po.Region;
import com.sz.admin.monitor.pojo.po.Substation;
import com.sz.admin.monitor.pojo.vo.tree.CameraNodeVO;
import com.sz.admin.monitor.pojo.vo.tree.ComputerRoomNodeVO;
import com.sz.admin.monitor.pojo.vo.tree.NvrNodeVO;
import com.sz.admin.monitor.pojo.vo.tree.TreeNodeVO;
import com.sz.admin.monitor.service.TreeService;
@ -33,8 +38,6 @@ public class TreeServiceImpl implements TreeService {
private NvrMapper nvrMapper;
@Resource
private CameraMapper cameraMapper;
@Resource
private ComputeRoomMapper computeRoomMapper;
@Override
public List<TreeNodeVO> buildTree() {
@ -65,37 +68,6 @@ public class TreeServiceImpl implements TreeService {
return treeRootList;
}
@Override
public List<TreeNodeVO> buildTreeComputeRoom() {
// 查询出所有的边电站
List<Substation> substationList = substationMapper.selectAll();
List<TreeNodeVO> treeRootList = new ArrayList<>();
for (Substation substation : substationList) {
TreeNodeVO rootNode = new TreeNodeVO(substation.getId(), substation.getName(), 1, null);
List<TreeNodeVO> computeChildren = loadChildrenForRoom(substation.getId());
rootNode.setChildren(computeChildren);
treeRootList.add(rootNode);
}
return treeRootList;
}
private List<TreeNodeVO> loadChildrenForRoom(Long id) {
QueryWrapper computeRoomQuery = new QueryWrapper();
computeRoomQuery.eq("substation_id", id);
List<ComputerRoom> computeRoomList = computeRoomMapper.selectListByQuery(computeRoomQuery);
List<TreeNodeVO> children = new ArrayList<>();
for (ComputerRoom computeRoom : computeRoomList) {
ComputerRoomNodeVO computeRoomNode =new ComputerRoomNodeVO();
computeRoomNode.setId(computeRoom.getId());
computeRoomNode.setName(computeRoom.getName());
computeRoomNode.setType(4);
computeRoomNode.setSubstationId(computeRoom.getSubstationId());
computeRoomNode.setChildren(null);
children.add(computeRoomNode);
}
return children;
}
private List<TreeNodeVO> loadChildrenForRegion(Long id) {
QueryWrapper childRegionQuery = new QueryWrapper();
childRegionQuery.eq("parent_id", id);

@ -1,34 +0,0 @@
package com.sz.admin.monitor.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.sz.admin.monitor.mapper.SubstationMapper;
import com.sz.admin.monitor.mapper.UserModelContentMapper;
import com.sz.admin.monitor.pojo.dto.dataModel.ModelContentDTO;
import com.sz.admin.monitor.pojo.po.Substation;
import com.sz.admin.monitor.pojo.po.UserModelContentDO;
import com.sz.admin.monitor.service.SubstationService;
import com.sz.admin.monitor.service.UserModelContentService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @author xq
* @description:
* @date 2026/3/13 10:23
*/
@Service
@RequiredArgsConstructor
public class UserModelContentServiceImpl extends ServiceImpl<UserModelContentMapper, UserModelContentDO> implements UserModelContentService {
@Override
public void saveOrUpdateModel(List<ModelContentDTO> list) {
if(CollectionUtil.isEmpty(list))
return;
for (ModelContentDTO modelContentDTO : list) {
ModelContentDTO.dtoConvertDO(modelContentDTO);
}
}
}

@ -8,18 +8,25 @@ import com.sz.admin.monitor.mapper.CameraAlarmMapper;
import com.sz.admin.monitor.mapper.CameraMapper;
import com.sz.admin.monitor.mapper.NvrMapper;
import com.sz.admin.monitor.pojo.dto.edgebox.BoxAlarmReportDto;
import com.sz.admin.monitor.pojo.po.*;
import com.sz.admin.monitor.pojo.po.Camera;
import com.sz.admin.monitor.pojo.po.CameraAlarm;
import com.sz.admin.monitor.pojo.po.Nvr;
import com.sz.admin.monitor.pojo.po.Preset;
import com.sz.admin.monitor.pojo.vo.camerasnapshot.CameraSnapshotVO;
import com.sz.admin.monitor.sdk.ManageNVR;
import com.sz.admin.monitor.service.*;
import com.sz.admin.monitor.utils.*;
import com.sz.admin.monitor.service.CameraService;
import com.sz.admin.monitor.service.CameraSnapshotService;
import com.sz.admin.monitor.service.ForwardService;
import com.sz.admin.monitor.service.PresetService;
import com.sz.admin.monitor.utils.ImageNameUtils;
import com.sz.admin.monitor.utils.OffsetCalculator;
import com.sz.admin.monitor.utils.RobustImageMatcherUtil;
import com.sz.admin.system.pojo.dto.sysmessage.Message;
import com.sz.admin.system.service.SysMessageService;
import com.sz.core.common.entity.SocketMessage;
import com.sz.core.common.entity.TransferMessage;
import com.sz.core.common.enums.MessageTransferScopeEnum;
import com.sz.core.common.enums.SocketChannelEnum;
import com.sz.core.util.JsonUtils;
import com.sz.redis.WebsocketRedisService;
import com.sz.security.core.util.LoginUtils;
import jakarta.annotation.Resource;
@ -33,14 +40,10 @@ import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
/**
* ClassName: ScheduleTask
@ -68,10 +71,7 @@ public class ScheduledTask {
private SysMessageService sysMessageService;
@Resource
private ForwardService forwardService;
@Resource
private BlurDetectionService blurDetectionService;
@Resource(name = "cameraInspectionExecutor")
private Executor cameraInspectionExecutor;
// @Scheduled(cron = "0/5 * * * * ?")
public void test()
@ -89,209 +89,10 @@ public class ScheduledTask {
msg.setToPushAll(true);
websocketRedisService.sendServiceToWs(msg);
}
// 定期模糊检测
// @Scheduled(cron = "0/20 * * * * ?")
public void executeFuzzyDetection() {
log.info("执行模糊检测");
// 遍历模糊检测表
List<BlurDetection> blurDetectionList = blurDetectionService.list();
// 过滤掉关闭的状态
blurDetectionList = blurDetectionList.stream().filter(blurDetection -> blurDetection.getEnable() == 1).toList();
if (blurDetectionList.isEmpty()) {
log.info("无启用的模糊检测任务");
return;
}
int count = 0;
for (BlurDetection blurDetection : blurDetectionList) {
Long cameraId = blurDetection.getCameraId();
try {
Camera camera = cameraService.getById(cameraId);
// 获取基准图
String imagePath1 = camera.getHomeImagePath();
// 检查基准图是否存在
boolean isExists = true;
if (imagePath1 == null || imagePath1.trim().isEmpty()) {
log.warn("摄像头[{}] 未设置基准图路径", cameraId);
isExists = false;
} else {
File file1 = new File(imagePath1);
if (!file1.exists() || !file1.isFile()) {
log.error("基准图文件物理路径不存在:{}", imagePath1);
isExists = false;
}
}
// 抓取当前图
String filename = ImageNameUtils.generateFileName("CAPTURE", cameraId);
String imagePath2 = manageNVR.capturePic(cameraId.intValue(), filename);
if (imagePath2 == null || imagePath2.isEmpty()) {
log.error("抓拍图片失败,摄像头 ID: {}", cameraId);
continue;
}
// 检查抓拍图是否存在
File file2 = new File(imagePath2);
if (!file2.exists() || !file2.isFile()) {
log.error("抓拍图不存在:{}", imagePath2);
continue;
}
count++;
// 进行单一背景检测
// BackgroundDetector.DetectResult result = BackgroundDetector.detectBackground(imagePath2);
// if("1".equals(result.value))
// {
// // 当前摄像头面对的是白墙,直接进行报警
// handleAnalysisResult(blurDetection, imagePath2, result);
// continue;
// }
// 进行模糊图片对比
BlurDetectorV4.DetectResult detectResult = null;
if (isExists) {
// 基准图存在,使用双图对比功能
// 检查是否对着墙壁或者特征不明显的地方
BlurrinessComparator.DetectResult result = BlurrinessComparator.compareBlurriness(imagePath1, imagePath2);
if (result.value.equals("1")) {
// 当前摄像头面对的是白墙,直接进行报警
handleAnalysisResult(blurDetection, imagePath2, result);
continue;
}
detectResult = BlurDetectorV4.doubleDetectBlur(imagePath1, imagePath2);
} else {
// 基准图不存在,使用单图对比功能
detectResult = BlurDetectorV4.detectBlur(imagePath2);
}
log.info("模糊检测结果:{}", detectResult);
handleAnalysisResult(blurDetection, imagePath1, imagePath2, detectResult, isExists);
log.info("模糊检测完成,共检测{}个摄像头", count);
} catch (Exception e) {
log.error("摄像头 ID [{}] 模糊检测过程中发生未知异常", cameraId, e);
}
}
}
//Result{type='bpmh', value='1', code='2000', desc='模糊 score=39.82', conf=1.0, score=39.82, ratio=0.00}
public record DetectResult(String desc,double score) {
}
// 分析背景检测结果并且报警
private void handleAnalysisResult(BlurDetection blurDetection, String imagePath, BlurrinessComparator.DetectResult result) throws IOException {
Camera camera = cameraService.getById(blurDetection.getCameraId());
CameraAlarm cameraAlarm = new CameraAlarm();
cameraAlarm.setCameraId(camera.getId());
Nvr nvr = nvrMapper.selectOneById(camera.getNvrId());
cameraAlarm.setAlarmType(6);
cameraAlarm.setAlarmAreaId(nvr.getStationId());
cameraAlarm.setChannelId(camera.getChannelId());
cameraAlarm.setCameraNo(camera.getCameraNo());
cameraAlarm.setCameraName(camera.getName());
cameraAlarm.setCaptureImage(imagePath);
DetectResult backgroundResult = new DetectResult(result.desc, result.score);
cameraAlarm.setAlgoResult(JsonUtils.toJsonString(backgroundResult));
cameraAlarm.setStatus(0);
cameraAlarmMapper.insert(cameraAlarm);
SocketMessage bean = new SocketMessage();
Map<String, Object> data = new HashMap<>();
data.put("title", "摄像头报警");
data.put("content", "摄像头[" + camera.getId() + "]发生模糊,请及时处理!");
bean.setData(JSON.toJSONString(data));
bean.setChannel(SocketChannelEnum.MESSAGE);
bean.setScope(MessageTransferScopeEnum.SOCKET_CLIENT);
TransferMessage msg = new TransferMessage();
msg.setMessage(bean);
msg.setFromUser("system");
msg.setToPushAll(true);
websocketRedisService.sendServiceToWs(msg);
BoxAlarmReportDto reportDto = new BoxAlarmReportDto();
// 获取时间字符串
String timeStr = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime localDateTime = LocalDateTime.parse(timeStr, timeFormatter);
// 转成时间戳(毫秒)
long timestamp = localDateTime.toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
reportDto.setAlarmTime(timestamp);
reportDto.setAlarmTypeDesc(AlarmReportEnums.BLUR_ALARM.getAlarmDescription());
byte[] CaptureFileContent = Files.readAllBytes(Paths.get(imagePath));
reportDto.setAlarmImage(Base64.getEncoder().encodeToString(CaptureFileContent));
reportDto.setCameraName(camera.getName());
reportDto.setAlarmType(AlarmReportEnums.BLUR_ALARM.getAlarmCode());
reportDto.setCameraId(camera.getId());
reportDto.setCameraNo(camera.getCameraNo());
try {
forwardService.enrichAndForward(blurDetection.getUrl(),reportDto);
}catch (Exception e)
{
log.error("模糊报警上报失败,摄像头 ID: {}, 相机编号:{}",
camera.getId(), camera.getCameraNo(), e);
}
}
// 分析模糊算法并且报警
private void handleAnalysisResult(BlurDetection blurDetection, String imagePath1, String imagePath2, BlurDetectorV4.DetectResult detectResult, boolean isExists) throws IOException {
if ("1".equals(detectResult.value)) {
// 发生了模糊,需要保存报警信息等人工处理
Camera camera = cameraService.getById(blurDetection.getCameraId());
CameraAlarm cameraAlarm = new CameraAlarm();
cameraAlarm.setCameraId(camera.getId());
// 设置告警区域
// 根据摄像头查询它对应的nvr
Nvr nvr = nvrMapper.selectOneById(camera.getNvrId());
// 设置告警区域
cameraAlarm.setAlarmType(6);
cameraAlarm.setAlarmAreaId(nvr.getStationId());
cameraAlarm.setChannelId(camera.getChannelId());
cameraAlarm.setCameraNo(camera.getCameraNo());
cameraAlarm.setCameraName(camera.getName());
cameraAlarm.setBaseImage(imagePath1);
cameraAlarm.setCaptureImage(imagePath2);
// 算法的结果
DetectResult result = new DetectResult(detectResult.desc, detectResult.score);
cameraAlarm.setAlgoResult(JsonUtils.toJsonString(result));
// 状态为未处理
cameraAlarm.setStatus(0);
cameraAlarmMapper.insert(cameraAlarm);
// log.info("生成模糊报警: 通道{}, 模糊信息: {}", camera.getChannelId(), detectResult.getDescription());
// 向前端主动推送消息
SocketMessage bean = new SocketMessage();
Map<String, Object> data = new HashMap<>();
data.put("title", "摄像头报警");
data.put("content", "摄像头[" + camera.getId() + "]发生模糊,请及时处理!");
bean.setData(JSON.toJSONString(data));
bean.setChannel(SocketChannelEnum.MESSAGE);
bean.setScope(MessageTransferScopeEnum.SOCKET_CLIENT);
TransferMessage msg = new TransferMessage();
msg.setMessage(bean);
msg.setFromUser("system");
msg.setToPushAll(true);
websocketRedisService.sendServiceToWs(msg);
// 统一处理报警信息后,将摄像头信息+报警信息上报给别人服务器
BoxAlarmReportDto reportDto = new BoxAlarmReportDto();
// 获取时间字符串
String timeStr = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime localDateTime = LocalDateTime.parse(timeStr, timeFormatter);
// 转成时间戳(毫秒)
long timestamp = localDateTime.toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
reportDto.setAlarmTime(timestamp);
reportDto.setAlarmTypeDesc(AlarmReportEnums.BLUR_ALARM.getAlarmDescription());
if(isExists && imagePath1 !=null) {
byte[] BaseFileContent = Files.readAllBytes(Paths.get(imagePath1));
reportDto.setBaseImage(Base64.getEncoder().encodeToString(BaseFileContent));
}
byte[] CaptureFileContent = Files.readAllBytes(Paths.get(imagePath2));
reportDto.setAlarmImage(Base64.getEncoder().encodeToString(CaptureFileContent));
reportDto.setCameraName(camera.getName());
reportDto.setAlarmType(AlarmReportEnums.BLUR_ALARM.getAlarmCode());
reportDto.setCameraId(camera.getId());
reportDto.setCameraNo(camera.getCameraNo());
try {
forwardService.enrichAndForward(blurDetection.getUrl(),reportDto);
}catch (Exception e)
{
log.error("模糊报警上报失败,摄像头 ID: {}, 相机编号:{}",
camera.getId(), camera.getCameraNo(), e);
}
}
}
// 定期检查
@Scheduled(cron = "0/20 * * * * ?")
public void executeInspection() throws InterruptedException {
@SneakyThrows
public void executeInspection() {
// 从摄像头表中获取设置了抓图对比和在线的摄像头
List<Camera> cameraList = cameraService.selectListByInspection();
// 然后根据摄像头id查询设置为标准位的预置位
@ -303,53 +104,36 @@ public class ScheduledTask {
}
List<Preset> presetList = presetService.selectByCameraIds(cameraIds);
for (Preset preset : presetList) {
CompletableFuture.runAsync(() -> {
// 调用具体的单次检测逻辑
processSinglePreset(preset);
}, cameraInspectionExecutor).exceptionally(e -> {
// 捕获线程池级别或执行过程中的严重异常
log.error("预置位[{}]异步巡检任务执行失败", preset.getPresetId(), e);
return null;
});
}
}
private void processSinglePreset(Preset preset) {
Long cameraId = preset.getCameraId();
try {
boolean ok = manageNVR.ptzPresets(cameraId.intValue(), 39, String.valueOf(preset.getPresetId()), preset.getPresetName());
// 这里的 sleep 现在是在独立的异步线程中执行的,不会阻塞主线程!
// 调用SDK让摄像头回归到该预置位
boolean ok = manageNVR.ptzPresets(preset.getCameraId().intValue(), 39, String.valueOf(preset.getPresetId()), preset.getPresetName());
// 延时5秒
Thread.sleep(5000);
if (ok) {
String filename = ImageNameUtils.generateFileName("CAPTURE", cameraId);
String image2 = manageNVR.capturePic(cameraId.intValue(), filename);
//String image2="D:\\work\\images\\CAPTURE_258_20260331104152476_de86220b.jpg";
// 抓图合法性校验
if (image2 == null || image2.isBlank()) {
log.error("摄像机[{}] 抓拍失败,无法获取当前图片", cameraId);
return; // 结束当前预置位的处理
}
CameraSnapshotVO cameraSnapshotVO = cameraSnapshotService.selectByCameraIdAndPresetIndex(cameraId, preset.getPresetId());
if (cameraSnapshotVO == null || cameraSnapshotVO.getImagePath() == null || cameraSnapshotVO.getImagePath().isBlank()) {
log.warn("摄像机[{}] 未设置预置位基准图,无法对比", cameraId);
return;
// 抓取一张图片
String filename = ImageNameUtils.generateFileName("CAPTURE",preset.getCameraId());
//String image2 = "D:\\work\\images\\CAPTURE_367_20260212092406664_2d117293.jpg";
// String image2 = "D:\\work\\images\\CAPTURE_19_20260302152457977_db33f5a8.jpg";
String image2 = manageNVR.capturePic(preset.getCameraId().intValue(), filename);
// 从图片表中根据预置位摄像机id类型为巡检为条件查询出基准图
CameraSnapshotVO cameraSnapshotVO = cameraSnapshotService.selectByCameraIdAndPresetIndex(preset.getCameraId(), preset.getPresetId());
if (cameraSnapshotVO == null || cameraSnapshotVO.getImagePath() == null) {
log.warn("摄像机[{}] 未设置基准图,无法对比", preset.getCameraId());
continue;
}
// 调用图片对比算法,校验摄像头是否移位
// 基准图
String image1 = cameraSnapshotVO.getImagePath();
// 对比
Map<String, Object> map = RobustImageMatcherUtil.calculateOffset(image1, image2);
log.info("摄像机[{}] 预置位[{}] 对比结果:{}", cameraId, preset.getPresetId(), map);
if (map != null && map.containsKey("value")) {
handleAnalysisResult(preset, image1, image2, map);
}
log.info("图片对比结果:{}", map);
// 判断结果并报警
handleAnalysisResult(preset, cameraSnapshotVO.getImagePath(), image2, map);
}
} catch (InterruptedException ie) {
log.error("摄像机[{}] 等待云台转动被中断", cameraId);
Thread.currentThread().interrupt();
} catch (Exception e) {
log.error("摄像机[{}] 预置位[{}] 巡检过程中发生业务异常", cameraId, preset.getPresetId(), e);
}
}
//判断移位算法结果并报警
//判断结果并报警
private void handleAnalysisResult(Preset preset, String imagePath, String image2, Map<String, Object> map) throws IOException {
int value = (int) map.get("value");
if (value == 1) {
@ -363,7 +147,6 @@ public class ScheduledTask {
// 设置告警区域
cameraAlarm.setAlarmAreaId(nvr.getStationId());
cameraAlarm.setChannelId(camera.getChannelId());
cameraAlarm.setCameraNo(camera.getCameraNo());
cameraAlarm.setCameraName(camera.getName());
cameraAlarm.setPresetIndex(preset.getPresetId());
// 标记为移位报警
@ -391,29 +174,18 @@ public class ScheduledTask {
websocketRedisService.sendServiceToWs(msg);
// 统一处理报警信息后,将摄像头信息+报警信息上报给别人服务器
BoxAlarmReportDto reportDto = new BoxAlarmReportDto();
// 获取时间字符串
String timeStr = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime localDateTime = LocalDateTime.parse(timeStr, timeFormatter);
// 转成时间戳(毫秒)
long timestamp = localDateTime.toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
reportDto.setAlarmTime(timestamp);
reportDto.setAlarmTypeDesc(AlarmReportEnums.DISPLACEMENT_ALARM.getAlarmDescription());
reportDto.setAlarmReportTime(LocalDateTime.now());
reportDto.setAlarmReportType(AlarmReportEnums.DISPLACEMENT_ALARM.getAlarmDescription());
reportDto.setDescription("摄像头发生移位");
byte[] BaseFileContent = Files.readAllBytes(Paths.get(imagePath));
reportDto.setBaseImage(Base64.getEncoder().encodeToString(BaseFileContent));
byte[] CaptureFileContent = Files.readAllBytes(Paths.get(image2));
reportDto.setAlarmImage(Base64.getEncoder().encodeToString(CaptureFileContent));
reportDto.setCaptureImage(Base64.getEncoder().encodeToString(CaptureFileContent));
reportDto.setUrl(preset.getPresetUrl());
reportDto.setCameraName(camera.getName());
reportDto.setCameraId(camera.getId());
reportDto.setAlarmType(AlarmReportEnums.DISPLACEMENT_ALARM.getAlarmCode());
reportDto.setCameraNo(camera.getCameraNo());
try {
forwardService.enrichAndForward(preset.getPresetUrl(),reportDto);
}catch (Exception e)
{
log.error("移位报警上报失败,摄像头 ID: {}, 相机编号:{}",
camera.getId(), camera.getCameraNo(), e);
}
forwardService.enrichAndForward(reportDto);
}
}

@ -96,7 +96,6 @@ public class AiBoxRequestUtil {
JSONObject responseJSON = new JSONObject();
responseJSON.put("code", CommonResponseEnum.MEDIA_HTTP_FAIL.getCode());
responseJSON.put("msg", CommonResponseEnum.MEDIA_HTTP_FAIL.getMessage());
MediaType JSON_TYPE = MediaType.parse("application/json; charset=utf-8");
String jsonString = "";
if (param != null) {

@ -1,234 +0,0 @@
package com.sz.admin.monitor.utils;
import org.bytedeco.javacpp.indexer.FloatIndexer;
import org.bytedeco.javacpp.indexer.IntIndexer;
import org.bytedeco.javacpp.indexer.UByteIndexer;
import org.bytedeco.opencv.opencv_core.Mat;
import org.bytedeco.opencv.opencv_core.MatVector;
import org.bytedeco.opencv.opencv_core.Size;
import org.bytedeco.opencv.opencv_core.TermCriteria;
import java.util.HashMap;
import java.util.Map;
import static org.bytedeco.opencv.global.opencv_core.*;
import static org.bytedeco.opencv.global.opencv_imgcodecs.imread;
import static org.bytedeco.opencv.global.opencv_imgproc.*;
public class BackgroundDetector {
// ===============================
// 统一的对外返回结果类 (与 V3/V4 一致)
// ===============================
public static class DetectResult {
public String type;
public String value;
public String code;
public String desc;
public double conf;
public double score;
public double ratio;
public DetectResult(String type, String value, String code, String desc, double conf, double score, double ratio) {
this.type = type;
this.value = value;
this.code = code;
this.desc = desc;
this.conf = conf;
this.score = score;
this.ratio = ratio;
}
@Override
public String toString() {
return String.format("Result{type='%s', value='%s', code='%s', desc='%s', conf=%.1f, score=%.2f, ratio=%.2f}",
type, value, code, desc, conf, score, ratio);
}
}
// ===============================
// 内部使用的算法基础数据封装
// ===============================
private static class InternalDetectionResult {
public boolean hasLargeBackground;
public double continuousBackgroundRatio;
public double totalMaxLabelRatio;
public Map<String, Integer> mainBackgroundColor;
}
// ===============================
// 对外暴露的公共访问方法 (默认参数)
// ===============================
public static DetectResult detectBackground(String imagePath) {
// 默认参数设定
int nClusters = 3;
double areaThreshold = 0.55;
int kernelSize = 5;
boolean useGray = false;
double clusterThreshold = 0.55;
return detectBackground(imagePath, nClusters, areaThreshold, kernelSize, useGray, clusterThreshold);
}
// ===============================
// 对外暴露的公共访问方法 (支持自定义参数)
// ===============================
public static DetectResult detectBackground(String imagePath, int nClusters, double areaThreshold,
int kernelSize, boolean useGray, double clusterThreshold) {
Mat img = imread(imagePath);
if (img == null || img.empty()) {
throw new IllegalArgumentException("无法读取图片或图片不存在: " + imagePath);
}
try {
// 调用核心算法
InternalDetectionResult internalResult = detectLargeContinuousBackground(
img, nClusters, areaThreshold, kernelSize, useGray, clusterThreshold);
// 将连续背景占比转化为 0-100 的 score 形式便于理解
double score = internalResult.continuousBackgroundRatio * 100.0;
// 拼接详细描述信息
String desc = String.format("连续背景占比:%.2f%%, 最大聚类占比:%.2f%%, 主颜色:%s",
score,
internalResult.totalMaxLabelRatio * 100.0,
internalResult.mainBackgroundColor.toString());
String detectTypeKey = "background";
if (internalResult.hasLargeBackground) {
// value="1" 表示检测出大片连续背景 (异常状态)code 给 4001
return new DetectResult(detectTypeKey, "1", "4001", "大片连续/纯色背景 [" + desc + "]", 1.0, score, 0.0);
} else {
// value="0" 表示正常图片
return new DetectResult(detectTypeKey, "0", "2000", "正常背景 [" + desc + "]", 1.0, score, 0.0);
}
} finally {
// 确保释放读取的图片内存
img.close();
}
}
// ===============================
// 核心算法实现:基于颜色聚类+连通域
// ===============================
private static InternalDetectionResult detectLargeContinuousBackground(
Mat img, int nClusters, double areaThreshold, int kernelSize, boolean useGray, double clusterThreshold) {
int h = img.rows();
int w = img.cols();
int totalPixels = h * w;
Mat processed = new Mat();
Mat reshaped32f = new Mat();
// 1. 预处理
if (useGray) {
cvtColor(img, processed, COLOR_BGR2GRAY);
Mat kernel = getStructuringElement(MORPH_RECT, new Size(kernelSize, kernelSize));
morphologyEx(processed, processed, MORPH_OPEN, kernel);
} else {
GaussianBlur(img, processed, new Size(3, 3), 0);
}
// 重塑为K-Means输入格式
Mat reshaped = processed.reshape(1, totalPixels);
reshaped.convertTo(reshaped32f, CV_32F);
// 2. K-Means颜色聚类
Mat labels = new Mat();
Mat centers = new Mat();
TermCriteria criteria = new TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 10, 1.0);
kmeans(reshaped32f, nClusters, labels, criteria, 10, KMEANS_PP_CENTERS, centers);
// 3. 统计最大类
int[] labelCounts = new int[nClusters];
IntIndexer labelIndexer = labels.createIndexer();
for (int i = 0; i < totalPixels; i++) {
int label = labelIndexer.get(i, 0);
labelCounts[label]++;
}
int maxLabel = 0;
int maxCount = 0;
for (int i = 0; i < nClusters; i++) {
if (labelCounts[i] > maxCount) {
maxCount = labelCounts[i];
maxLabel = i;
}
}
double maxLabelRatio = (double) maxCount / totalPixels;
// 4. 生成最大类的掩码并分析连通域
Mat mask = new Mat(h, w, CV_8UC1);
UByteIndexer maskIndexer = mask.createIndexer();
int pixelIdx = 0;
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
int label = labelIndexer.get(pixelIdx++, 0);
maskIndexer.put(y, x, label == maxLabel ? 255 : 0);
}
}
MatVector contours = new MatVector();
Mat hierarchy = new Mat();
findContours(mask, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
double largestContourArea = 0;
for (int i = 0; i < contours.size(); i++) {
double area = contourArea(contours.get(i));
if (area > largestContourArea) {
largestContourArea = area;
}
}
double continuousBackgroundRatio = largestContourArea / totalPixels;
boolean hasLargeBackground = (continuousBackgroundRatio >= areaThreshold) && (maxLabelRatio >= clusterThreshold);
// 5. 提取主颜色
FloatIndexer centersIndexer = centers.createIndexer();
Map<String, Integer> mainColor = new HashMap<>();
if (useGray) {
mainColor.put("gray", (int) centersIndexer.get(maxLabel, 0));
} else {
mainColor.put("B", (int) centersIndexer.get(maxLabel, 0));
mainColor.put("G", (int) centersIndexer.get(maxLabel, 1));
mainColor.put("R", (int) centersIndexer.get(maxLabel, 2));
}
// 6. 构造内部返回结果
InternalDetectionResult result = new InternalDetectionResult();
result.hasLargeBackground = hasLargeBackground;
result.continuousBackgroundRatio = continuousBackgroundRatio;
result.totalMaxLabelRatio = maxLabelRatio;
result.mainBackgroundColor = mainColor;
// 释放临时对象
processed.close();
reshaped32f.close();
reshaped.close();
labels.close();
centers.close();
mask.close();
hierarchy.close();
return result;
}
// ===============================
// 测试入口
// ===============================
public static void main(String[] args) {
// 替换为你的真实测试图片路径
String path = "D:\\work\\ceshi\\bai.jpg";
try {
System.out.println("===== 开始检测 =====");
// 直接调用极简的封装方法
DetectResult result = detectBackground(path);
System.out.println(result.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
}

@ -1,326 +0,0 @@
package com.sz.admin.monitor.utils;
import org.bytedeco.opencv.opencv_core.*;
import org.bytedeco.opencv.opencv_imgproc.*;
import static org.bytedeco.opencv.global.opencv_core.*;
import static org.bytedeco.opencv.global.opencv_imgcodecs.*;
import static org.bytedeco.opencv.global.opencv_imgproc.*;
public class BlurDetectorV2 {
// ===============================
// 数据返回类 (替代 Python 中的 dict)
// ===============================
public static class DetectResult {
public String type;
public String value;
public String code;
public String desc;
public double conf;
public double score; // 显式记录分数,替代正则解析
public double ratio; // 双图比较时的比率
public DetectResult(String type, String value, String code, String desc, double conf, double score, double ratio) {
this.type = type;
this.value = value;
this.code = code;
this.desc = desc;
this.conf = conf;
this.score = score;
this.ratio = ratio;
}
@Override
public String toString() {
return String.format("Result{value='%s', code='%s', desc='%s'}", value, code, desc);
}
}
// ===============================
// 光照归一化
// ===============================
private static Mat normalizeLighting(Mat gray) {
Mat dst = new Mat();
try (CLAHE clahe = createCLAHE(3.0, new Size(8, 8))) {
clahe.apply(gray, dst);
}
return dst;
}
// ===============================
// Laplacian 清晰度
// ===============================
private static double laplacianScore(Mat gray) {
try (Mat lap = new Mat(); Mat mean = new Mat(); Mat stddev = new Mat()) {
Laplacian(gray, lap, CV_64F);
meanStdDev(lap, mean, stddev);
double sd = stddev.createIndexer().getDouble();
return sd * sd; // 方差
}
}
// ===============================
// Tenengrad 梯度
// ===============================
private static double tenengradScore(Mat gray) {
try (Mat gx = new Mat(); Mat gy = new Mat();
Mat gx2 = new Mat(); Mat gy2 = new Mat();
Mat g = new Mat()) {
Sobel(gray, gx, CV_64F, 1, 0, 3, 1.0, 0.0, BORDER_DEFAULT);
Sobel(gray, gy, CV_64F, 0, 1, 3, 1.0, 0.0, BORDER_DEFAULT);
multiply(gx, gx, gx2);
multiply(gy, gy, gy2);
add(gx2, gy2, g);
return mean(g).get(0);
}
}
// ===============================
// Edge Density边缘密度
// ===============================
private static double edgeDensity(Mat gray) {
try (Mat edges = new Mat()) {
Canny(gray, edges, 80, 150);
int nonZero = countNonZero(edges);
return (double) nonZero / (edges.rows() * edges.cols());
}
}
// ===============================
// FFT 高频能量 (最终完美版)
// ===============================
private static double fftHighFrequency(Mat gray) {
// 第一层:初始化所有基础操作矩阵,这些变量在此块内不可被重新赋值
try (Mat f32 = new Mat();
Mat zeros = Mat.zeros(gray.size(), CV_32F).asMat();
MatVector planes = new MatVector(2);
Mat complexI = new Mat();
Mat mag = new Mat();
Mat ones = Mat.ones(gray.size(), CV_32F).asMat();
Mat tmp = new Mat()) {
// 1. 转为浮点并合并为双通道复数矩阵
gray.convertTo(f32, CV_32F);
planes.put(0, f32);
planes.put(1, zeros);
merge(planes, complexI);
// 2. 傅里叶变换
dft(complexI, complexI);
// 3. 计算幅度: magnitude = log(abs(fshift) + 1)
split(complexI, planes);
magnitude(planes.get(0), planes.get(1), mag);
add(mag, ones, mag);
log(mag, mag);
// 4. fftshift 平移 (将低频移到中心)
// 声明新的变量 croppedMag 来接收裁剪结果,避免给 mag 重新赋值引发报错
try (Mat croppedMag = new Mat(mag, new Rect(0, 0, mag.cols() & -2, mag.rows() & -2))) {
int cx = croppedMag.cols() / 2;
int cy = croppedMag.rows() / 2;
// 第三层:声明所有的子矩阵 (ROI) 和 mask确保它们用完后被自动释放
try (Mat q0 = new Mat(croppedMag, new Rect(0, 0, cx, cy));
Mat q1 = new Mat(croppedMag, new Rect(cx, 0, cx, cy));
Mat q2 = new Mat(croppedMag, new Rect(0, cy, cx, cy));
Mat q3 = new Mat(croppedMag, new Rect(cx, cy, cx, cy));
Mat mask = new Mat(croppedMag.size(), CV_32F, new Scalar(1.0))) {
// 象限交换 (Shift)
q0.copyTo(tmp);
q3.copyTo(q0);
tmp.copyTo(q3);
q1.copyTo(tmp);
q2.copyTo(q1);
tmp.copyTo(q2);
// 5. Mask 屏蔽中心低频
int r = Math.min(cx, cy) / 4;
rectangle(mask,
new Point(cx - r, cy - r),
new Point(cx + r, cy + r),
new Scalar(0.0),
-1, 8, 0);
// 6. 对应元素相乘 (只保留高频部分)
multiply(croppedMag, mask, croppedMag);
// 7. 计算整体高频均值
return mean(croppedMag).get(0);
}
}
}
}
// ===============================
// 白墙和纯色背景图检测
// ===============================
private static boolean isWhiteWall(Mat image) {
try (Mat hsv = new Mat();
Mat mask = new Mat();
Mat lower = new Mat(1, 1, CV_8UC3, new Scalar(0, 0, 200, 0));
Mat upper = new Mat(1, 1, CV_8UC3, new Scalar(180, 40, 255, 0));
Mat gray = new Mat()) {
cvtColor(image, hsv, COLOR_BGR2HSV);
inRange(hsv, lower, upper, mask);
double ratio = (double) countNonZero(mask) / (mask.rows() * mask.cols());
cvtColor(image, gray, COLOR_BGR2GRAY);
double lap = laplacianScore(gray);
double edge = edgeDensity(gray);
if (ratio > 0.7 && lap < 15) {
return true;
}
if (lap < 10 && edge < 0.02) {
return true;
}
return false;
}
}
// ===============================
// ROI中心区域 (切片等价实现)
// ===============================
private static Mat extractRoi(Mat image) {
int h = image.rows();
int w = image.cols();
int y = h / 5;
int x = w / 5;
int roiH = (4 * h / 5) - y;
int roiW = (4 * w / 5) - x;
return new Mat(image, new Rect(x, y, roiW, roiH));
}
// ===============================
// 模糊检测单图主函数
// ===============================
public static DetectResult detectBlur(Mat image, String detectTypeKey, double threshold) {
if (image == null || image.empty()) {
throw new IllegalArgumentException("image read error");
}
// ===============================
// 白墙过滤
// ===============================
if (isWhiteWall(image)) {
return new DetectResult(detectTypeKey, "1", "4001", "白墙/纯色背景 score=0", 0.0, 0.0, 0.0);
}
try (Mat roiImage = extractRoi(image);
Mat gray = new Mat()) {
cvtColor(roiImage, gray, COLOR_BGR2GRAY);
try (Mat normGray = normalizeLighting(gray)) {
// ===============================
// 计算指标
// ===============================
double lap = laplacianScore(normGray);
double ten = tenengradScore(normGray);
double edge = edgeDensity(normGray);
double fft = fftHighFrequency(normGray);
// ===============================
// 归一化
// ===============================
double lap_n = Math.min(lap / 2000.0, 1.0);
double ten_n = Math.min(ten / 10000.0, 1.0);
double edge_n = Math.min(edge * 10.0, 1.0);
double fft_n = Math.min(fft / 10.0, 1.0);
// ===============================
// 综合评分
// ===============================
double score = (0.35 * lap_n + 0.30 * ten_n + 0.20 * edge_n + 0.15 * fft_n) * 100.0;
String descStr = String.format("score=%.2f", score);
// ===============================
// 判断
// ===============================
if (score < threshold) {
return new DetectResult(detectTypeKey, "1", "2000", "模糊 " + descStr, 1.0, score, 0.0);
} else {
return new DetectResult(detectTypeKey, "0", "2000", "正常 " + descStr, 1.0, score, 0.0);
}
}
}
}
public static DetectResult detectBlur(String imagePath) {
try (Mat img = imread(imagePath)) {
return detectBlur(img, "bpmh", 90.0);
}
}
// ===============================
// 双图检测
// ===============================
public static DetectResult doubleDetectBlur(String imgPath, String refPath, double ratioThreshold) {
boolean useRef = true;
if (refPath == null || refPath.isEmpty()) {
useRef = false;
}
try (Mat imgMat = imread(imgPath);
Mat refMat = useRef ? imread(refPath) : null) {
if (refMat == null || refMat.empty()) {
useRef = false;
}
// 单图模式兜底
if (!useRef) {
return detectBlur(imgMat, "bpmh", 60.0);
}
// 双图模式
DetectResult imgResult = detectBlur(imgMat, "bpmh", 60.0);
DetectResult refResult = detectBlur(refMat, "bpmh", 60.0);
double scoreImg = imgResult.score;
double scoreRef = refResult.score;
if (scoreRef < 1e-6) {
return new DetectResult("bpmh", "0", "2000", "参考图异常", 0.0, scoreImg, 0.0);
}
double ratio = scoreRef / scoreImg;
System.out.println(scoreImg + ", " + scoreRef);
if (ratio < ratioThreshold) {
return new DetectResult("bpmh", "1", "2000", String.format("模糊 ratio=%.2f", ratio), 1.0, scoreImg, ratio);
} else {
return new DetectResult("bpmh", "0", "2000", String.format("正常 ratio=%.2f", ratio), 1.0, scoreImg, ratio);
}
}
}
// ===============================
// 测试入口
// ===============================
public static void main(String[] args) {
String img = "D:\\work\\images\\CAPTURE_265_20260319160327655_79b39c57.jpg"; // 基准图提前给定
String ref = "D:\\work\\images\\CAPTURE_266_20260319160512854_13b52e7e.jpg"; // 摄像头后面传入图像
//Result{value='0', code='2000', desc='正常 ratio=1.01'}
// Result{value='1', code='2000', desc='模糊 ratio=0.69'}
// 默认 ratio_threshold=0.85
// DetectResult result = doubleDetectBlur(img, ref, 0.85);
// 60 到 70
DetectResult detectResult = detectBlur(ref);
System.out.println("单图检测:"+detectResult.toString());
// System.out.println(result.toString());
}
}

@ -1,324 +0,0 @@
package com.sz.admin.monitor.utils;
import org.bytedeco.opencv.opencv_core.*;
import org.bytedeco.opencv.opencv_imgproc.CLAHE;
import static org.bytedeco.opencv.global.opencv_core.*;
import static org.bytedeco.opencv.global.opencv_imgcodecs.imread;
import static org.bytedeco.opencv.global.opencv_imgproc.*;
public class BlurDetectorV3 {
// ===============================
// 数据返回类 (替代 Python 中的 dict)
// ===============================
public static class DetectResult {
public String type;
public String value;
public String code;
public String desc;
public double conf;
public double score; // 显式记录分数,替代正则解析
public double ratio; // 双图比较时的比率
public DetectResult(String type, String value, String code, String desc, double conf, double score, double ratio) {
this.type = type;
this.value = value;
this.code = code;
this.desc = desc;
this.conf = conf;
this.score = score;
this.ratio = ratio;
}
@Override
public String toString() {
return String.format("Result{value='%s', code='%s', desc='%s'}", value, code, desc);
}
}
// ===============================
// 光照归一化
// ===============================
private static Mat normalizeLighting(Mat gray) {
Mat dst = new Mat();
try (CLAHE clahe = createCLAHE(1.5, new Size(8, 8))) {
clahe.apply(gray, dst);
}
return dst;
}
// ===============================
// Laplacian 清晰度
// ===============================
private static double laplacianScore(Mat gray) {
try (Mat lap = new Mat(); Mat mean = new Mat(); Mat stddev = new Mat()) {
Laplacian(gray, lap, CV_64F);
meanStdDev(lap, mean, stddev);
double sd = stddev.createIndexer().getDouble();
return sd * sd; // 方差
}
}
// ===============================
// Tenengrad 梯度
// ===============================
private static double tenengradScore(Mat gray) {
try (Mat gx = new Mat(); Mat gy = new Mat();
Mat gx2 = new Mat(); Mat gy2 = new Mat();
Mat g = new Mat()) {
Sobel(gray, gx, CV_64F, 1, 0, 3, 1.0, 0.0, BORDER_DEFAULT);
Sobel(gray, gy, CV_64F, 0, 1, 3, 1.0, 0.0, BORDER_DEFAULT);
multiply(gx, gx, gx2);
multiply(gy, gy, gy2);
add(gx2, gy2, g);
return mean(g).get(0);
}
}
// ===============================
// Edge Density边缘密度
// ===============================
private static double edgeDensity(Mat gray) {
try (Mat edges = new Mat()) {
Canny(gray, edges, 80, 150);
int nonZero = countNonZero(edges);
return (double) nonZero / (edges.rows() * edges.cols());
}
}
// ===============================
// FFT 高频能量 (最终完美版)
// ===============================
private static double fftHighFrequency(Mat gray) {
// 第一层:初始化所有基础操作矩阵,这些变量在此块内不可被重新赋值
try (Mat f32 = new Mat();
Mat zeros = Mat.zeros(gray.size(), CV_32F).asMat();
MatVector planes = new MatVector(2);
Mat complexI = new Mat();
Mat mag = new Mat();
Mat ones = Mat.ones(gray.size(), CV_32F).asMat();
Mat tmp = new Mat()) {
// 1. 转为浮点并合并为双通道复数矩阵
gray.convertTo(f32, CV_32F);
planes.put(0, f32);
planes.put(1, zeros);
merge(planes, complexI);
// 2. 傅里叶变换
dft(complexI, complexI);
// 3. 计算幅度: magnitude = log(abs(fshift) + 1)
split(complexI, planes);
magnitude(planes.get(0), planes.get(1), mag);
add(mag, ones, mag);
log(mag, mag);
// 4. fftshift 平移 (将低频移到中心)
// 声明新的变量 croppedMag 来接收裁剪结果,避免给 mag 重新赋值引发报错
try (Mat croppedMag = new Mat(mag, new Rect(0, 0, mag.cols() & -2, mag.rows() & -2))) {
int cx = croppedMag.cols() / 2;
int cy = croppedMag.rows() / 2;
// 第三层:声明所有的子矩阵 (ROI) 和 mask确保它们用完后被自动释放
try (Mat q0 = new Mat(croppedMag, new Rect(0, 0, cx, cy));
Mat q1 = new Mat(croppedMag, new Rect(cx, 0, cx, cy));
Mat q2 = new Mat(croppedMag, new Rect(0, cy, cx, cy));
Mat q3 = new Mat(croppedMag, new Rect(cx, cy, cx, cy));
Mat mask = new Mat(croppedMag.size(), CV_32F, new Scalar(1.0))) {
// 象限交换 (Shift)
q0.copyTo(tmp); q3.copyTo(q0); tmp.copyTo(q3);
q1.copyTo(tmp); q2.copyTo(q1); tmp.copyTo(q2);
// 5. Mask 屏蔽中心低频
int r = Math.min(cx, cy) / 4;
rectangle(mask,
new Point(cx - r, cy - r),
new Point(cx + r, cy + r),
new Scalar(0.0),
-1, 8, 0);
// 6. 对应元素相乘 (只保留高频部分)
multiply(croppedMag, mask, croppedMag);
// 7. 计算整体高频均值
return mean(croppedMag).get(0);
}
}
}
}
// ===============================
// 白墙和纯色背景图检测
// ===============================
private static boolean isWhiteWall(Mat image) {
try (Mat hsv = new Mat();
Mat mask = new Mat();
Mat lower = new Mat(1, 1, CV_8UC3, new Scalar(0, 0, 80, 0));
Mat upper = new Mat(1, 1, CV_8UC3, new Scalar(180, 50, 255, 0));
Mat gray = new Mat()) {
cvtColor(image, hsv, COLOR_BGR2HSV);
inRange(hsv, lower, upper, mask);
double ratio = (double) countNonZero(mask) / (mask.rows() * mask.cols());
cvtColor(image, gray, COLOR_BGR2GRAY);
double lap = laplacianScore(gray);
double edge = edgeDensity(gray);
if (ratio > 0.7 && lap < 30) {
return true;
}
if (lap < 15 && edge < 0.03) {
return true;
}
return false;
}
}
// ===============================
// ROI中心区域 (切片等价实现)
// ===============================
private static Mat extractRoi(Mat image) {
int h = image.rows();
int w = image.cols();
int y = h / 5;
int x = w / 5;
int roiH = (4 * h / 5) - y;
int roiW = (4 * w / 5) - x;
return new Mat(image, new Rect(x, y, roiW, roiH));
}
// ===============================
// 模糊检测单图主函数
// ===============================
public static DetectResult detectBlur(Mat image, String detectTypeKey, double threshold) {
if (image == null || image.empty()) {
throw new IllegalArgumentException("image read error");
}
// ===============================
// 白墙过滤
// ===============================
if (isWhiteWall(image)) {
return new DetectResult(detectTypeKey, "1", "4001", "白墙/纯色背景 score=0", 0.0, 0.0, 0.0);
}
try (Mat roiImage = extractRoi(image);
Mat gray = new Mat()) {
cvtColor(roiImage, gray, COLOR_BGR2GRAY);
try (Mat normGray = normalizeLighting(gray);
Mat smoothedGray = new Mat()) {
GaussianBlur(normGray, smoothedGray, new Size(3, 3), 0);
// ===============================
// 计算指标
// ===============================
double lap = laplacianScore(normGray);
double ten = tenengradScore(normGray);
double edge = edgeDensity(normGray);
double fft = fftHighFrequency(normGray);
// ===============================
// 归一化
// ===============================
double lap_n = Math.min(lap / 2000.0, 1.0);
double ten_n = Math.min(ten / 10000.0, 1.0);
double edge_n = Math.min(edge * 10.0, 1.0);
double fft_n = Math.min(fft / 10.0, 1.0);
// ===============================
// 综合评分
// ===============================
double score = (0.35 * lap_n + 0.30 * ten_n + 0.20 * edge_n + 0.15 * fft_n) * 100.0;
String descStr = String.format("score=%.2f", score);
// ===============================
// 判断
// ===============================
if (score < threshold) {
return new DetectResult(detectTypeKey, "1", "2000", "模糊 " + descStr, 1.0, score, 0.0);
} else {
return new DetectResult(detectTypeKey, "0", "2000", "正常 " + descStr, 1.0, score, 0.0);
}
}
}
}
public static DetectResult detectBlur(String imagePath) {
try (Mat img = imread(imagePath)) {
return detectBlur(img, "bpmh", 70.0);
}
}
// ===============================
// 双图检测
// ===============================
public static DetectResult doubleDetectBlur(String imgPath, String refPath, double ratioThreshold) {
boolean useRef = true;
if (refPath == null || refPath.isEmpty()) {
useRef = false;
}
try (Mat imgMat = imread(imgPath);
Mat refMat = useRef ? imread(refPath) : null) {
if (refMat == null || refMat.empty()) {
useRef = false;
}
// 单图模式兜底
if (!useRef) {
return detectBlur(imgMat, "bpmh", 60.0);
}
// 双图模式
DetectResult imgResult = detectBlur(imgMat, "bpmh", 60.0);
DetectResult refResult = detectBlur(refMat, "bpmh", 60.0);
double scoreImg = imgResult.score;
double scoreRef = refResult.score;
if (scoreRef < 1e-6) {
return new DetectResult("bpmh", "0", "2000", "参考图异常", 0.0, scoreImg, 0.0);
}
double ratio = scoreRef / scoreImg;
System.out.println(scoreImg + ", " + scoreRef);
if (ratio < ratioThreshold) {
return new DetectResult("bpmh", "1", "2000", String.format("模糊 ratio=%.2f", ratio), 1.0, scoreImg, ratio);
} else {
return new DetectResult("bpmh", "0", "2000", String.format("正常 ratio=%.2f", ratio), 1.0, scoreImg, ratio);
}
}
}
// ===============================
// 测试入口
// ===============================
public static void main(String[] args) {
String img = "D:\\work\\images\\CAPTURE_258_20260320093827912_bcf0b101.jpg"; // 基准图提前给定
String ref = "D:\\work\\images\\CAPTURE_258_20260319160231459_89b5826f.jpg"; // 摄像头后面传入图像
// 默认 ratio_threshold=0.85
DetectResult result = doubleDetectBlur(img, ref, 0.85);
System.out.println(result.toString());
// String path="D:\\work\\imgs\\CAPTURE_258_20260319160154580_4d8abac8.jpg";
// DetectResult detectResult = detectBlur(path);
// System.out.println(detectResult.toString());
}
}

@ -1,297 +0,0 @@
package com.sz.admin.monitor.utils;
import org.bytedeco.opencv.opencv_core.*;
import org.bytedeco.opencv.opencv_imgproc.CLAHE;
import static org.bytedeco.opencv.global.opencv_core.*;
import static org.bytedeco.opencv.global.opencv_imgcodecs.imread;
import static org.bytedeco.opencv.global.opencv_imgproc.*;
public class BlurDetectorV4 {
// ===============================
// 数据返回类 (从 V3 移植)
// ===============================
public static class DetectResult {
public String type;
public String value;
public String code;
public String desc;
public double conf;
public double score;
public double ratio;
public DetectResult(String type, String value, String code, String desc, double conf, double score, double ratio) {
this.type = type;
this.value = value;
this.code = code;
this.desc = desc;
this.conf = conf;
this.score = score;
this.ratio = ratio;
}
@Override
public String toString() {
return String.format("Result{type='%s', value='%s', code='%s', desc='%s', conf=%.1f, score=%.2f, ratio=%.2f}",
type, value, code, desc, conf, score, ratio);
}
}
// ===============================
// 光照归一化 (保留 V4 的 3.0 参数)
// ===============================
public static Mat normalizeLighting(Mat gray) {
CLAHE clahe = createCLAHE(1.5, new Size(8, 8));
Mat dst = new Mat();
clahe.apply(gray, dst);
clahe.close();
return dst;
}
// ===============================
// Laplacian 清晰度
// ===============================
public static double laplacianScore(Mat gray) {
Mat lap = new Mat();
Laplacian(gray, lap, CV_64F);
Mat mean = new Mat();
Mat stddev = new Mat();
meanStdDev(lap, mean, stddev);
double std = stddev.createIndexer().getDouble(0);
lap.close();
mean.close();
stddev.close();
return std * std;
}
// ===============================
// Tenengrad 梯度
// ===============================
public static double tenengradScore(Mat gray) {
Mat gx = new Mat();
Mat gy = new Mat();
Sobel(gray, gx, CV_64F, 1, 0, 3, 1.0, 0.0, BORDER_DEFAULT);
Sobel(gray, gy, CV_64F, 0, 1, 3, 1.0, 0.0, BORDER_DEFAULT);
Mat gx2 = new Mat();
Mat gy2 = new Mat();
Mat g = new Mat();
multiply(gx, gx, gx2);
multiply(gy, gy, gy2);
add(gx2, gy2, g);
double score = mean(g).get(0);
gx.close(); gy.close();
gx2.close(); gy2.close(); g.close();
return score;
}
// ===============================
// Edge Density边缘密度
// ===============================
public static double edgeDensity(Mat gray) {
Mat edges = new Mat();
Canny(gray, edges, 80, 150);
int nonZero = countNonZero(edges);
long totalPixels = edges.total();
edges.close();
return (double) nonZero / totalPixels;
}
// ===============================
// FFT 高频能量 (保留 V4 优秀的内存管理)
// ===============================
private static double fftHighFrequency(Mat gray) {
try (Mat f32 = new Mat();
Mat zeros = Mat.zeros(gray.size(), CV_32F).asMat();
MatVector planes = new MatVector(2);
Mat complexI = new Mat();
Mat mag = new Mat();
Mat ones = Mat.ones(gray.size(), CV_32F).asMat();
Mat tmp = new Mat()) {
gray.convertTo(f32, CV_32F);
planes.put(0, f32);
planes.put(1, zeros);
merge(planes, complexI);
dft(complexI, complexI);
split(complexI, planes);
magnitude(planes.get(0), planes.get(1), mag);
add(mag, ones, mag);
log(mag, mag);
try (Mat croppedMag = new Mat(mag, new Rect(0, 0, mag.cols() & -2, mag.rows() & -2))) {
int cx = croppedMag.cols() / 2;
int cy = croppedMag.rows() / 2;
try (Mat q0 = new Mat(croppedMag, new Rect(0, 0, cx, cy));
Mat q1 = new Mat(croppedMag, new Rect(cx, 0, cx, cy));
Mat q2 = new Mat(croppedMag, new Rect(0, cy, cx, cy));
Mat q3 = new Mat(croppedMag, new Rect(cx, cy, cx, cy));
Mat mask = new Mat(croppedMag.size(), CV_32F, new Scalar(1.0))) {
q0.copyTo(tmp); q3.copyTo(q0); tmp.copyTo(q3);
q1.copyTo(tmp); q2.copyTo(q1); tmp.copyTo(q2);
int r = Math.min(cx, cy) / 4;
rectangle(mask,
new Point(cx - r, cy - r),
new Point(cx + r, cy + r),
new Scalar(0.0),
-1, 8, 0);
multiply(croppedMag, mask, croppedMag);
return mean(croppedMag).get(0);
}
}
}
}
// ===============================
// ROI中心区域
// ===============================
public static Mat extractRoi(Mat image) {
int h = image.rows();
int w = image.cols();
int x = w / 5;
int y = h / 5;
int roiW = (4 * w / 5) - x;
int roiH = (4 * h / 5) - y;
return new Mat(image, new Rect(x, y, roiW, roiH));
}
// ===============================
// 模糊检测主函数 (改为返回 DetectResult)
// ===============================
public static DetectResult detectBlur(Object imageObj, String detectTypeKey, double threshold) {
Mat image = null;
boolean needsRelease = false;
if (imageObj instanceof String) {
image = imread((String) imageObj);
needsRelease = true;
} else if (imageObj instanceof Mat) {
image = (Mat) imageObj;
}
if (image == null || image.empty()) {
throw new IllegalArgumentException("Image read error");
}
Mat roiImage = extractRoi(image);
Mat gray = new Mat();
cvtColor(roiImage, gray, COLOR_BGR2GRAY);
Mat normalizedGray = normalizeLighting(gray);
double lap = laplacianScore(normalizedGray);
double ten = tenengradScore(normalizedGray);
double edge = edgeDensity(normalizedGray);
double fft = fftHighFrequency(normalizedGray);
double lapN = Math.min(lap / 2000.0, 1.0);
double tenN = Math.min(ten / 10000.0, 1.0);
double edgeN = Math.min(edge * 10.0, 1.0);
double fftN = Math.min(fft / 10.0, 1.0);
double score = (0.35 * lapN + 0.30 * tenN + 0.20 * edgeN + 0.15 * fftN) * 100;
String descStr = String.format("score=%.2f", score);
DetectResult result;
if (score < threshold) {
result = new DetectResult(detectTypeKey, "1", "2000", "模糊 " + descStr, 1.0, score, 0.0);
} else {
result = new DetectResult(detectTypeKey, "0", "2000", "正常 " + descStr, 1.0, score, 0.0);
}
roiImage.close();
gray.close();
normalizedGray.close();
if (needsRelease) image.close();
return result;
}
public static DetectResult detectBlur(Object imageObj) {
return detectBlur(imageObj, "bpmh", 70.0);
}
// ===============================
// 双图检测 (改为返回 DetectResult)
// ===============================
public static DetectResult doubleDetectBlur(Object imgObj, Object refObj, double ratioThreshold) {
boolean useRef = true;
Mat refMat = null;
boolean refNeedsRelease = false;
if (refObj == null || (refObj instanceof String && ((String) refObj).isEmpty())) {
useRef = false;
} else if (refObj instanceof String) {
refMat = imread((String) refObj);
if (refMat == null || refMat.empty()) useRef = false;
else refNeedsRelease = true;
} else if (refObj instanceof Mat) {
refMat = (Mat) refObj;
if (refMat.empty()) useRef = false;
}
// 单图模式兜底
if (!useRef) {
return detectBlur(imgObj);
}
// 双图模式
DetectResult imgResult = detectBlur(imgObj);
DetectResult refResult = detectBlur(refMat);
double scoreImg = imgResult.score;
double scoreRef = refResult.score;
if (scoreRef < 1e-6) {
if (refNeedsRelease) refMat.close();
return new DetectResult("bpmh", "0", "2000", "参考图异常", 0.0, scoreImg, 0.0);
}
double ratio = scoreRef / scoreImg;
DetectResult finalResult;
if (ratio < ratioThreshold) {
finalResult = new DetectResult("bpmh", "1", "2000", String.format("模糊 ratio=%.2f", ratio), 1.0, scoreImg, ratio);
} else {
finalResult = new DetectResult("bpmh", "0", "2000", String.format("正常 ratio=%.2f", ratio), 1.0, scoreImg, ratio);
}
if (refNeedsRelease) refMat.close();
return finalResult;
}
public static DetectResult doubleDetectBlur(Object imgObj, Object refObj) {
return doubleDetectBlur(imgObj, refObj, 0.85);
}
// ===============================
// 测试
// ===============================
public static void main(String[] args) {
String path = "D:\\work\\imgs\\CAPTURE_266_20260317123416804_ac0f924c.jpg";
try {
DetectResult result = detectBlur(path);
System.out.println(result.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
}

@ -1,136 +0,0 @@
package com.sz.admin.monitor.utils;
import org.im4java.core.IMOperation;
import org.im4java.core.ImageCommand;
import java.io.File;
import java.util.UUID;
public class BlurrinessComparator {
// 强制指定你的 IM7 安装路径
private static final String IM_PATH = "D:\\work\\imageMagic\\ImageMagick-7.1.2-Q16-HDRI";
public static void main(String[] args) {
String imagePath1 = "D:\\work\\images\\CAPTURE_258_20260320093827912_bcf0b101.jpg"; // 基准图 (清晰图标准)
String imagePath2 = "D:\\work\\images\\CAPTURE_258_20260319160020647_ffbc4f66.jpg"; // 当前图 (待检测图)
try {
//Result{type='blur_detection', value='1', code='BLURRY', desc='检测到图片模糊',
// conf=1.0, score=580.00, ratio=0.47}
DetectResult result = compareBlurriness(imagePath1, imagePath2);
// 打印最终返回的标准结果对象
System.out.println("检测完成,返回结果: " + result.toString());
} catch (Exception e) {
System.err.println("处理图片时发生错误: " + e.getMessage());
e.printStackTrace();
}
}
/**
*
*/
public static class DetectResult {
public String type; // 检测类型
public String value; // 结果值 (1: 模糊, 0: 正常)
public String code; // 结果代码
public String desc; // 描述信息
public double conf; // 置信度 (此处默认给 1.0)
public double score; // 得分 (此处记录目标图压缩后的大小)
public double ratio; // 比例 (目标图大小 / 基准图大小)
public DetectResult(String type, String value, String code, String desc, double conf, double score, double ratio) {
this.type = type;
this.value = value;
this.code = code;
this.desc = desc;
this.conf = conf;
this.score = score;
this.ratio = ratio;
}
@Override
public String toString() {
return String.format("Result{type='%s', value='%s', code='%s', desc='%s', conf=%.1f, score=%.2f, ratio=%.2f}",
type, value, code, desc, conf, score, ratio);
}
}
/**
* DetectResult
*/
public static DetectResult compareBlurriness(String path1, String path2) {
System.out.println("正在处理ImageMagick 缩放压缩 -> 计算文件体积...");
// 注意:尺寸设为 64x48 非常小,可能会导致压缩后体积差异微弱。如果效果不明显建议调大(如 800x600)。
long size1 = getProcessedImageSize(path1, 64, 48, 80.0);
long size2 = getProcessedImageSize(path2, 64, 48, 80.0);
System.out.println("图片1大小: " + size1 + " 字节, 图片2大小: " + size2 + " 字节");
// x为图片的大小 y = 90.733x - 35.474;
// 90.733*1.234-35.474 = 76.490522
// 90.733*0.58-35.474 = 17.15114
// 90.733*0.695-35.474 = 27.585435
// size1/size2
// size1>size2 则出现了模糊,因为特征点变小了,体积也变小了
// 计算体积比例 (防止除以 0 的异常)
double ratio = (size1 == 0) ? 1.0 : (double) size2 / size1;
String value;
String code;
String desc;
// 判断逻辑:当前图体积小于基准图,说明丢失了细节,判定为模糊
if (size2 < size1) {
value = "1"; // 模糊 Value 为 1
code = "BLURRY";
desc = "检测到图片模糊";
} else {
value = "0"; // 正常 Value 为 0
code = "NORMAL";
desc = "图片清晰度正常";
}
// 构造并返回标准实体类
// type: 设为 "blur_detection"
// conf: 置信度设为 1.0
// score: 存入 size2 (目标图片的压缩字节数) 作为参考得分
return new DetectResult("blur_detection", value, code, desc, 1.0, size2, ratio);
}
/**
* ImageMagick
*/
private static long getProcessedImageSize(String imagePath, int width, int height, double quality) {
// 生成一个临时文件路径,用于存放 ImageMagick 处理后的图片
String tempImagePath = System.getProperty("java.io.tmpdir") + File.separator + "im_temp_" + UUID.randomUUID() + ".jpg";
File tempFile = new File(tempImagePath);
try {
// 构建 ImageMagick 压缩指令
IMOperation op = new IMOperation();
op.addImage(imagePath);
op.strip(); // 剥离 EXIF 等多余元数据信息
op.resize(width, height); // 缩放为标准大小
op.quality(quality); // 设置 JPEG 压缩质量
op.addImage(tempImagePath); // 输出到临时文件
// 执行命令
ImageCommand cmd = new ImageCommand("magick");
cmd.setSearchPath(IM_PATH);
cmd.run(op);
// 获取临时文件的体积 (字节)
if (!tempFile.exists()) {
throw new RuntimeException("ImageMagick 未能成功生成临时文件!");
}
return tempFile.length();
} catch (Exception e) {
throw new RuntimeException("ImageMagick 处理流水线执行失败", e);
} finally {
// 务必清理临时文件
if (tempFile.exists()) {
tempFile.delete();
}
}
}
}

@ -28,7 +28,7 @@ import static org.bytedeco.opencv.global.opencv_video.calcOpticalFlowFarneback;
/**
* ClassName: RobustImageMatcherUtil
* Package: com.sz.admin.monitor.utils
* Description:
* Description:
*/
@Slf4j
public class RobustImageMatcherUtil {
@ -321,6 +321,7 @@ public class RobustImageMatcherUtil {
private static Map<String, Object> formatResult(MatchResult result, Mat img1, Mat img2) {
Map<String, Object> map = new LinkedHashMap<>();
map.put("type", "tx_yzwpy");
if (!result.success || result.confidence < 0.2) {
log.warn("图像配准失败或置信度过低");
map.put("value", 1);
@ -387,16 +388,10 @@ public class RobustImageMatcherUtil {
Mat[] preprocessed = preprocess(smallImg1, smallImg2);
Mat gray1 = preprocessed[0];
Mat gray2 = preprocessed[1];
Map<String, Object> map = new LinkedHashMap<>();
map.put("type", "tx_yzwpy");
if (!isSameScene(gray1, gray2)) {
log.warn("图像差异过大,非同一场景: path1={}, path2={}", path1, path2);
map.put("value", 1);
map.put("code", "4000");
OffsetVO offsetVO = new OffsetVO();
offsetVO.setDescription("图像存在偏移");
map.put("description", JsonUtils.toJsonString(offsetVO));
return map;
return null;
}
MatchResult flowResult = calcOpticalFlow(gray1, gray2);
@ -421,8 +416,8 @@ public class RobustImageMatcherUtil {
// String img1Path = "D:\\work\\images\\CAPTURE_118_20260306140433490_7c323ce1.jpg";
// String img2Path = "D:\\work\\images\\CAPTURE_118_20260306140555121_0a31347e.jpg";
// 出现移位的图片 value 1
String img1Path = "D:\\work\\temp\\history_20260324_180000.jpg";
String img2Path = "D:\\work\\temp\\history_20260324_190000.jpg";
String img1Path="D:\\work\\images\\CAPTURE_118_20260306140328178_7f050439.jpg";
String img2Path="D:\\work\\images\\CAPTURE_118_20260306140433490_7c323ce1.jpg";
Map<String, Object> map = RobustImageMatcherUtil.calculateOffset(img1Path, img2Path);
System.out.println(map);
}

@ -2,41 +2,24 @@ package com.sz.admin.monitor.utils;
import java.io.File;
/**
* ClassName: UrlConvert
* Package: com.sz.admin.monitor.utils
* Description:
*/
public class UrlConvert {
private static final String RESOURCE_PREFIX = "/save/";
// 定义我们配置的两个静态资源根目录 (注意统一用正斜杠 / )
private static final String BASE_DIR_IMAGES = "D:/work/images/";
private static final String BASE_DIR_TEMP = "D:/work/temp/";
/**
*
* @param localPath : D:\work\temp\task_123\test.jpg
* @return /save/task_123/test.jpg
* @param localPath D:\work\sz-admin\images\test.jpg
* @return /save/test.jpg
*/
public static String convertToUrl(String localPath) {
if (localPath == null || localPath.isEmpty()) {
return null;
}
// 1. 统一路径分隔符:将 Windows 的反斜杠 \ 全部替换为标准的正斜杠 /
String normalizedPath = localPath.replace("\\", "/");
String relativePath = "";
// 2. 剥离根目录,保留后面的子文件夹和文件名
if (normalizedPath.startsWith(BASE_DIR_IMAGES)) {
// 截取掉 D:/work/images/ 这一部分
relativePath = normalizedPath.substring(BASE_DIR_IMAGES.length());
} else if (normalizedPath.startsWith(BASE_DIR_TEMP)) {
// 截取掉 D:/work/temp/ 这一部分,保留 task_123/test.jpg
relativePath = normalizedPath.substring(BASE_DIR_TEMP.length());
} else {
// 兜底方案:如果传进来的路径不在这两个目录里,退回到只取文件名
relativePath = new File(localPath).getName();
}
// 3. 拼接映射前缀
return RESOURCE_PREFIX + relativePath;
String fileName = new File(localPath).getName();
// 2. 拼接映射前缀
return RESOURCE_PREFIX + fileName;
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save