新增AIoT边缘智能模块:摄像头ROI配置、算法管理、配置推送
- 后端:新增aiot模块(bean/dao/service/controller),支持ROI区域CRUD、 算法注册表管理、ROI-算法绑定、配置推送到FastAPI边缘端、变更日志 - 前端:新增摄像头配置页(列表+ROI子页面)、算法配置页、Canvas绘图组件 (矩形/多边形)、动态算法参数编辑器、ZLM截图作为ROI编辑背景 - 数据库:新建4张表(wvp_ai_roi/algorithm/roi_algo_bind/config_log) 字段与FastAPI端SQLite兼容,含2个预置算法 - 路由裁剪:隐藏无关菜单(地图/部标/推流/录制计划等) - 修复cameraId含/导致REST路径解析错误(改用query参数) - 新增ai.service配置项(边缘端地址/超时/开关) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
package com.genersoft.iot.vmp.aiot.bean;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Schema(description = "算法注册表")
|
||||
public class AiAlgorithm {
|
||||
|
||||
@Schema(description = "数据库自增ID")
|
||||
private Integer id;
|
||||
|
||||
@Schema(description = "算法编码")
|
||||
private String algoCode;
|
||||
|
||||
@Schema(description = "算法名称")
|
||||
private String algoName;
|
||||
|
||||
@Schema(description = "目标类别")
|
||||
private String targetClass;
|
||||
|
||||
@Schema(description = "参数模板JSON")
|
||||
private String paramSchema;
|
||||
|
||||
@Schema(description = "描述")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "是否可用")
|
||||
private Integer isActive;
|
||||
|
||||
@Schema(description = "最后同步时间")
|
||||
private String syncTime;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private String createTime;
|
||||
|
||||
@Schema(description = "更新时间")
|
||||
private String updateTime;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.genersoft.iot.vmp.aiot.bean;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Schema(description = "配置变更日志")
|
||||
public class AiConfigLog {
|
||||
|
||||
@Schema(description = "数据库自增ID")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "类型:ROI/ALGORITHM/BIND")
|
||||
private String configType;
|
||||
|
||||
@Schema(description = "目标ID")
|
||||
private String configId;
|
||||
|
||||
@Schema(description = "变更前JSON")
|
||||
private String oldValue;
|
||||
|
||||
@Schema(description = "变更后JSON")
|
||||
private String newValue;
|
||||
|
||||
@Schema(description = "操作人")
|
||||
private String updatedBy;
|
||||
|
||||
@Schema(description = "操作时间")
|
||||
private String updatedAt;
|
||||
}
|
||||
54
src/main/java/com/genersoft/iot/vmp/aiot/bean/AiRoi.java
Normal file
54
src/main/java/com/genersoft/iot/vmp/aiot/bean/AiRoi.java
Normal file
@@ -0,0 +1,54 @@
|
||||
package com.genersoft.iot.vmp.aiot.bean;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Schema(description = "ROI区域配置")
|
||||
public class AiRoi {
|
||||
|
||||
@Schema(description = "数据库自增ID")
|
||||
private Integer id;
|
||||
|
||||
@Schema(description = "UUID,与FastAPI端同步用")
|
||||
private String roiId;
|
||||
|
||||
@Schema(description = "通道国标编号")
|
||||
private String cameraId;
|
||||
|
||||
@Schema(description = "WVP通道表ID")
|
||||
private Integer channelDbId;
|
||||
|
||||
@Schema(description = "设备国标编号")
|
||||
private String deviceId;
|
||||
|
||||
@Schema(description = "ROI名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "形状:rectangle/polygon")
|
||||
private String roiType;
|
||||
|
||||
@Schema(description = "JSON归一化坐标")
|
||||
private String coordinates;
|
||||
|
||||
@Schema(description = "显示颜色")
|
||||
private String color;
|
||||
|
||||
@Schema(description = "优先级")
|
||||
private Integer priority;
|
||||
|
||||
@Schema(description = "启用状态")
|
||||
private Integer enabled;
|
||||
|
||||
@Schema(description = "扩展参数JSON")
|
||||
private String extraParams;
|
||||
|
||||
@Schema(description = "描述")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private String createTime;
|
||||
|
||||
@Schema(description = "更新时间")
|
||||
private String updateTime;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.genersoft.iot.vmp.aiot.bean;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Schema(description = "ROI算法绑定")
|
||||
public class AiRoiAlgoBind {
|
||||
|
||||
@Schema(description = "数据库自增ID")
|
||||
private Integer id;
|
||||
|
||||
@Schema(description = "UUID,同步用")
|
||||
private String bindId;
|
||||
|
||||
@Schema(description = "ROI的UUID")
|
||||
private String roiId;
|
||||
|
||||
@Schema(description = "算法编码")
|
||||
private String algoCode;
|
||||
|
||||
@Schema(description = "自定义参数JSON")
|
||||
private String params;
|
||||
|
||||
@Schema(description = "优先级")
|
||||
private Integer priority;
|
||||
|
||||
@Schema(description = "启用状态")
|
||||
private Integer enabled;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private String createTime;
|
||||
|
||||
@Schema(description = "更新时间")
|
||||
private String updateTime;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.genersoft.iot.vmp.aiot.bean;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@Schema(description = "ROI详情(含关联算法)")
|
||||
public class AiRoiDetail {
|
||||
|
||||
@Schema(description = "ROI基本信息")
|
||||
private AiRoi roi;
|
||||
|
||||
@Schema(description = "绑定的算法列表")
|
||||
private List<AiRoiAlgoBindDetail> algorithms;
|
||||
|
||||
@Data
|
||||
@Schema(description = "算法绑定详情")
|
||||
public static class AiRoiAlgoBindDetail {
|
||||
private AiRoiAlgoBind bind;
|
||||
private AiAlgorithm algorithm;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.genersoft.iot.vmp.aiot.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "ai.service")
|
||||
public class AiServiceConfig {
|
||||
|
||||
private String url = "http://localhost:8090";
|
||||
|
||||
private int pushTimeout = 10000;
|
||||
|
||||
private boolean enabled = false;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.genersoft.iot.vmp.aiot.controller;
|
||||
|
||||
import com.genersoft.iot.vmp.aiot.bean.AiAlgorithm;
|
||||
import com.genersoft.iot.vmp.aiot.service.IAiAlgorithmService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/ai/algorithm")
|
||||
@Tag(name = "AI-算法管理")
|
||||
public class AiAlgorithmController {
|
||||
|
||||
@Autowired
|
||||
private IAiAlgorithmService algorithmService;
|
||||
|
||||
@Operation(summary = "查询算法列表")
|
||||
@GetMapping("/list")
|
||||
public List<AiAlgorithm> queryList() {
|
||||
return algorithmService.queryAll();
|
||||
}
|
||||
|
||||
@Operation(summary = "启用/禁用算法")
|
||||
@PostMapping("/toggle/{id}")
|
||||
public void toggleActive(@PathVariable Integer id, @RequestParam Integer isActive) {
|
||||
algorithmService.toggleActive(id, isActive);
|
||||
}
|
||||
|
||||
@Operation(summary = "从边缘端同步算法")
|
||||
@PostMapping("/sync")
|
||||
public void syncFromEdge() {
|
||||
algorithmService.syncFromEdge();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.genersoft.iot.vmp.aiot.controller;
|
||||
|
||||
import com.genersoft.iot.vmp.aiot.service.IAiConfigService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/ai/config")
|
||||
@Tag(name = "AI-配置推送")
|
||||
public class AiConfigController {
|
||||
|
||||
@Autowired
|
||||
private IAiConfigService configService;
|
||||
|
||||
@Operation(summary = "推送配置到边缘端")
|
||||
@PostMapping("/push")
|
||||
public void pushConfig(@RequestParam String cameraId) {
|
||||
configService.pushConfig(cameraId);
|
||||
}
|
||||
|
||||
@Operation(summary = "导出通道完整配置JSON")
|
||||
@GetMapping("/export")
|
||||
public Map<String, Object> exportConfig(@RequestParam String cameraId) {
|
||||
return configService.exportConfig(cameraId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.genersoft.iot.vmp.aiot.controller;
|
||||
|
||||
import com.genersoft.iot.vmp.aiot.bean.AiConfigLog;
|
||||
import com.genersoft.iot.vmp.aiot.service.IAiConfigLogService;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/ai/log")
|
||||
@Tag(name = "AI-变更日志")
|
||||
public class AiConfigLogController {
|
||||
|
||||
@Autowired
|
||||
private IAiConfigLogService configLogService;
|
||||
|
||||
@Operation(summary = "分页查询变更日志")
|
||||
@GetMapping("/list")
|
||||
public PageInfo<AiConfigLog> queryList(
|
||||
@Parameter(description = "配置类型") @RequestParam(required = false) String configType,
|
||||
@Parameter(description = "配置ID") @RequestParam(required = false) String configId,
|
||||
@RequestParam int page,
|
||||
@RequestParam int count) {
|
||||
return configLogService.queryList(configType, configId, page, count);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package com.genersoft.iot.vmp.aiot.controller;
|
||||
|
||||
import com.genersoft.iot.vmp.aiot.bean.AiRoi;
|
||||
import com.genersoft.iot.vmp.aiot.bean.AiRoiAlgoBind;
|
||||
import com.genersoft.iot.vmp.aiot.bean.AiRoiDetail;
|
||||
import com.genersoft.iot.vmp.aiot.service.IAiRoiService;
|
||||
import com.genersoft.iot.vmp.media.bean.MediaServer;
|
||||
import com.genersoft.iot.vmp.media.service.IMediaServerService;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.ServletOutputStream;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.compress.utils.IOUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/ai/roi")
|
||||
@Tag(name = "AI-ROI区域管理")
|
||||
public class AiRoiController {
|
||||
|
||||
@Autowired
|
||||
private IAiRoiService roiService;
|
||||
|
||||
@Autowired
|
||||
private IMediaServerService mediaServerService;
|
||||
|
||||
@Operation(summary = "分页查询ROI列表")
|
||||
@GetMapping("/list")
|
||||
public PageInfo<AiRoi> queryList(
|
||||
@Parameter(description = "通道国标编号") @RequestParam(required = false) String cameraId,
|
||||
@Parameter(description = "设备国标编号") @RequestParam(required = false) String deviceId,
|
||||
@Parameter(description = "关键字") @RequestParam(required = false) String query,
|
||||
@RequestParam int page,
|
||||
@RequestParam int count) {
|
||||
return roiService.queryList(cameraId, deviceId, query, page, count);
|
||||
}
|
||||
|
||||
@Operation(summary = "ROI详情(含关联算法)")
|
||||
@GetMapping("/{id}")
|
||||
public AiRoiDetail queryDetail(@PathVariable Integer id) {
|
||||
return roiService.queryDetail(id);
|
||||
}
|
||||
|
||||
@Operation(summary = "通道的所有ROI")
|
||||
@GetMapping("/channel")
|
||||
public List<AiRoi> queryByCameraId(@RequestParam String cameraId) {
|
||||
return roiService.queryByCameraId(cameraId);
|
||||
}
|
||||
|
||||
@Operation(summary = "新增或更新ROI")
|
||||
@PostMapping("/save")
|
||||
public void save(@RequestBody AiRoi roi) {
|
||||
roiService.save(roi);
|
||||
}
|
||||
|
||||
@Operation(summary = "删除ROI")
|
||||
@DeleteMapping("/delete/{roiId}")
|
||||
public void delete(@PathVariable String roiId) {
|
||||
roiService.delete(roiId);
|
||||
}
|
||||
|
||||
@Operation(summary = "绑定算法")
|
||||
@PostMapping("/bindAlgo")
|
||||
public void bindAlgo(@RequestBody AiRoiAlgoBind bind) {
|
||||
roiService.bindAlgo(bind);
|
||||
}
|
||||
|
||||
@Operation(summary = "解绑算法")
|
||||
@DeleteMapping("/unbindAlgo")
|
||||
public void unbindAlgo(@RequestParam String bindId) {
|
||||
roiService.unbindAlgo(bindId);
|
||||
}
|
||||
|
||||
@Operation(summary = "更新绑定参数")
|
||||
@PostMapping("/updateAlgoParams")
|
||||
public void updateAlgoParams(@RequestBody AiRoiAlgoBind bind) {
|
||||
roiService.updateAlgoParams(bind);
|
||||
}
|
||||
|
||||
@Operation(summary = "获取摄像头截图")
|
||||
@GetMapping("/snap")
|
||||
public void getSnap(HttpServletResponse resp,
|
||||
@RequestParam String app,
|
||||
@RequestParam String stream) {
|
||||
MediaServer mediaServer = mediaServerService.getDefaultMediaServer();
|
||||
if (mediaServer == null) {
|
||||
resp.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
|
||||
return;
|
||||
}
|
||||
String snapDir = "snap" + File.separator + "ai";
|
||||
String fileName = app + "_" + stream + ".jpg";
|
||||
// 调用ZLM截图
|
||||
mediaServerService.getSnap(mediaServer, app, stream, 10, 1, snapDir, fileName);
|
||||
// 等待截图生成
|
||||
File snapFile = new File(snapDir + File.separator + fileName);
|
||||
int retries = 0;
|
||||
while (!snapFile.exists() && retries < 50) {
|
||||
try {
|
||||
Thread.sleep(200);
|
||||
} catch (InterruptedException ignored) {}
|
||||
retries++;
|
||||
}
|
||||
if (!snapFile.exists()) {
|
||||
resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
InputStream in = Files.newInputStream(snapFile.toPath());
|
||||
resp.setContentType(MediaType.IMAGE_JPEG_VALUE);
|
||||
ServletOutputStream outputStream = resp.getOutputStream();
|
||||
IOUtils.copy(in, outputStream);
|
||||
in.close();
|
||||
outputStream.close();
|
||||
} catch (IOException e) {
|
||||
log.warn("[AI截图] 读取截图失败: {}", e.getMessage());
|
||||
resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.genersoft.iot.vmp.aiot.dao;
|
||||
|
||||
import com.genersoft.iot.vmp.aiot.bean.AiAlgorithm;
|
||||
import org.apache.ibatis.annotations.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface AiAlgorithmMapper {
|
||||
|
||||
@Select("SELECT * FROM wvp_ai_algorithm ORDER BY id")
|
||||
List<AiAlgorithm> queryAll();
|
||||
|
||||
@Select("SELECT * FROM wvp_ai_algorithm WHERE is_active=1 ORDER BY id")
|
||||
List<AiAlgorithm> queryActive();
|
||||
|
||||
@Select("SELECT * FROM wvp_ai_algorithm WHERE algo_code=#{algoCode}")
|
||||
AiAlgorithm queryByCode(@Param("algoCode") String algoCode);
|
||||
|
||||
@Select("SELECT * FROM wvp_ai_algorithm WHERE id=#{id}")
|
||||
AiAlgorithm queryById(@Param("id") Integer id);
|
||||
|
||||
@Insert("INSERT INTO wvp_ai_algorithm (algo_code, algo_name, target_class, param_schema, " +
|
||||
"description, is_active, sync_time, create_time, update_time) " +
|
||||
"VALUES (#{algoCode}, #{algoName}, #{targetClass}, #{paramSchema}, " +
|
||||
"#{description}, #{isActive}, #{syncTime}, #{createTime}, #{updateTime})")
|
||||
@Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
|
||||
int add(AiAlgorithm algorithm);
|
||||
|
||||
@Update("UPDATE wvp_ai_algorithm SET algo_name=#{algoName}, target_class=#{targetClass}, " +
|
||||
"param_schema=#{paramSchema}, description=#{description}, is_active=#{isActive}, " +
|
||||
"sync_time=#{syncTime}, update_time=#{updateTime} WHERE algo_code=#{algoCode}")
|
||||
int updateByCode(AiAlgorithm algorithm);
|
||||
|
||||
@Update("UPDATE wvp_ai_algorithm SET is_active=#{isActive}, update_time=#{updateTime} WHERE id=#{id}")
|
||||
int updateActive(@Param("id") Integer id, @Param("isActive") Integer isActive, @Param("updateTime") String updateTime);
|
||||
|
||||
@Delete("DELETE FROM wvp_ai_algorithm WHERE id=#{id}")
|
||||
int delete(@Param("id") Integer id);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.genersoft.iot.vmp.aiot.dao;
|
||||
|
||||
import com.genersoft.iot.vmp.aiot.bean.AiConfigLog;
|
||||
import org.apache.ibatis.annotations.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface AiConfigLogMapper {
|
||||
|
||||
@Insert("INSERT INTO wvp_ai_config_log (config_type, config_id, old_value, new_value, updated_by, updated_at) " +
|
||||
"VALUES (#{configType}, #{configId}, #{oldValue}, #{newValue}, #{updatedBy}, #{updatedAt})")
|
||||
@Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
|
||||
int add(AiConfigLog log);
|
||||
|
||||
@Select(value = {"<script>" +
|
||||
"SELECT * FROM wvp_ai_config_log WHERE 1=1 " +
|
||||
"<if test='configType != null'> AND config_type=#{configType}</if> " +
|
||||
"<if test='configId != null'> AND config_id=#{configId}</if> " +
|
||||
"ORDER BY id DESC" +
|
||||
"</script>"})
|
||||
List<AiConfigLog> queryList(@Param("configType") String configType,
|
||||
@Param("configId") String configId);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.genersoft.iot.vmp.aiot.dao;
|
||||
|
||||
import com.genersoft.iot.vmp.aiot.bean.AiRoiAlgoBind;
|
||||
import org.apache.ibatis.annotations.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface AiRoiAlgoBindMapper {
|
||||
|
||||
@Insert("INSERT INTO wvp_ai_roi_algo_bind (bind_id, roi_id, algo_code, params, priority, enabled, create_time, update_time) " +
|
||||
"VALUES (#{bindId}, #{roiId}, #{algoCode}, #{params}, #{priority}, #{enabled}, #{createTime}, #{updateTime})")
|
||||
@Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
|
||||
int add(AiRoiAlgoBind bind);
|
||||
|
||||
@Update("UPDATE wvp_ai_roi_algo_bind SET params=#{params}, priority=#{priority}, " +
|
||||
"enabled=#{enabled}, update_time=#{updateTime} WHERE bind_id=#{bindId}")
|
||||
int updateByBindId(AiRoiAlgoBind bind);
|
||||
|
||||
@Delete("DELETE FROM wvp_ai_roi_algo_bind WHERE bind_id=#{bindId}")
|
||||
int deleteByBindId(@Param("bindId") String bindId);
|
||||
|
||||
@Delete("DELETE FROM wvp_ai_roi_algo_bind WHERE roi_id=#{roiId}")
|
||||
int deleteByRoiId(@Param("roiId") String roiId);
|
||||
|
||||
@Select("SELECT * FROM wvp_ai_roi_algo_bind WHERE roi_id=#{roiId} ORDER BY priority DESC, id")
|
||||
List<AiRoiAlgoBind> queryByRoiId(@Param("roiId") String roiId);
|
||||
|
||||
@Select("SELECT * FROM wvp_ai_roi_algo_bind WHERE bind_id=#{bindId}")
|
||||
AiRoiAlgoBind queryByBindId(@Param("bindId") String bindId);
|
||||
|
||||
@Select("SELECT * FROM wvp_ai_roi_algo_bind WHERE roi_id=#{roiId} AND algo_code=#{algoCode}")
|
||||
AiRoiAlgoBind queryByRoiIdAndAlgoCode(@Param("roiId") String roiId, @Param("algoCode") String algoCode);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.genersoft.iot.vmp.aiot.dao;
|
||||
|
||||
import com.genersoft.iot.vmp.aiot.bean.AiRoi;
|
||||
import org.apache.ibatis.annotations.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface AiRoiMapper {
|
||||
|
||||
@Insert("INSERT INTO wvp_ai_roi (roi_id, camera_id, channel_db_id, device_id, name, roi_type, " +
|
||||
"coordinates, color, priority, enabled, extra_params, description, create_time, update_time) " +
|
||||
"VALUES (#{roiId}, #{cameraId}, #{channelDbId}, #{deviceId}, #{name}, #{roiType}, " +
|
||||
"#{coordinates}, #{color}, #{priority}, #{enabled}, #{extraParams}, #{description}, #{createTime}, #{updateTime})")
|
||||
@Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
|
||||
int add(AiRoi roi);
|
||||
|
||||
@Update("UPDATE wvp_ai_roi SET camera_id=#{cameraId}, channel_db_id=#{channelDbId}, device_id=#{deviceId}, " +
|
||||
"name=#{name}, roi_type=#{roiType}, coordinates=#{coordinates}, color=#{color}, " +
|
||||
"priority=#{priority}, enabled=#{enabled}, extra_params=#{extraParams}, " +
|
||||
"description=#{description}, update_time=#{updateTime} WHERE id=#{id}")
|
||||
int update(AiRoi roi);
|
||||
|
||||
@Delete("DELETE FROM wvp_ai_roi WHERE roi_id=#{roiId}")
|
||||
int deleteByRoiId(@Param("roiId") String roiId);
|
||||
|
||||
@Select("SELECT * FROM wvp_ai_roi WHERE id=#{id}")
|
||||
AiRoi queryById(@Param("id") Integer id);
|
||||
|
||||
@Select("SELECT * FROM wvp_ai_roi WHERE roi_id=#{roiId}")
|
||||
AiRoi queryByRoiId(@Param("roiId") String roiId);
|
||||
|
||||
@Select(value = {"<script>" +
|
||||
"SELECT * FROM wvp_ai_roi WHERE 1=1 " +
|
||||
"<if test='cameraId != null'> AND camera_id=#{cameraId}</if> " +
|
||||
"<if test='deviceId != null'> AND device_id=#{deviceId}</if> " +
|
||||
"<if test='query != null'> AND (name LIKE concat('%',#{query},'%') OR camera_id LIKE concat('%',#{query},'%'))</if> " +
|
||||
"ORDER BY priority DESC, id DESC" +
|
||||
"</script>"})
|
||||
List<AiRoi> queryList(@Param("cameraId") String cameraId,
|
||||
@Param("deviceId") String deviceId,
|
||||
@Param("query") String query);
|
||||
|
||||
@Select("SELECT * FROM wvp_ai_roi WHERE camera_id=#{cameraId} AND enabled=1 ORDER BY priority DESC")
|
||||
List<AiRoi> queryByCameraId(@Param("cameraId") String cameraId);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.genersoft.iot.vmp.aiot.service;
|
||||
|
||||
import com.genersoft.iot.vmp.aiot.bean.AiAlgorithm;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface IAiAlgorithmService {
|
||||
|
||||
List<AiAlgorithm> queryAll();
|
||||
|
||||
List<AiAlgorithm> queryActive();
|
||||
|
||||
void toggleActive(Integer id, Integer isActive);
|
||||
|
||||
void syncFromEdge();
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.genersoft.iot.vmp.aiot.service;
|
||||
|
||||
import com.genersoft.iot.vmp.aiot.bean.AiConfigLog;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
|
||||
public interface IAiConfigLogService {
|
||||
|
||||
void addLog(String configType, String configId, String oldValue, String newValue, String updatedBy);
|
||||
|
||||
PageInfo<AiConfigLog> queryList(String configType, String configId, int page, int count);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.genersoft.iot.vmp.aiot.service;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface IAiConfigService {
|
||||
|
||||
Map<String, Object> exportConfig(String cameraId);
|
||||
|
||||
void pushConfig(String cameraId);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.genersoft.iot.vmp.aiot.service;
|
||||
|
||||
import com.genersoft.iot.vmp.aiot.bean.AiRoi;
|
||||
import com.genersoft.iot.vmp.aiot.bean.AiRoiAlgoBind;
|
||||
import com.genersoft.iot.vmp.aiot.bean.AiRoiDetail;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface IAiRoiService {
|
||||
|
||||
void save(AiRoi roi);
|
||||
|
||||
void delete(String roiId);
|
||||
|
||||
AiRoiDetail queryDetail(Integer id);
|
||||
|
||||
List<AiRoi> queryByCameraId(String cameraId);
|
||||
|
||||
PageInfo<AiRoi> queryList(String cameraId, String deviceId, String query, int page, int count);
|
||||
|
||||
void bindAlgo(AiRoiAlgoBind bind);
|
||||
|
||||
void unbindAlgo(String bindId);
|
||||
|
||||
void updateAlgoParams(AiRoiAlgoBind bind);
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.genersoft.iot.vmp.aiot.service.impl;
|
||||
|
||||
import com.genersoft.iot.vmp.aiot.bean.AiAlgorithm;
|
||||
import com.genersoft.iot.vmp.aiot.config.AiServiceConfig;
|
||||
import com.genersoft.iot.vmp.aiot.dao.AiAlgorithmMapper;
|
||||
import com.genersoft.iot.vmp.aiot.service.IAiAlgorithmService;
|
||||
import com.genersoft.iot.vmp.aiot.service.IAiConfigLogService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class AiAlgorithmServiceImpl implements IAiAlgorithmService {
|
||||
|
||||
@Autowired
|
||||
private AiAlgorithmMapper algorithmMapper;
|
||||
|
||||
@Autowired
|
||||
private AiServiceConfig aiServiceConfig;
|
||||
|
||||
@Autowired
|
||||
private IAiConfigLogService configLogService;
|
||||
|
||||
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
@Override
|
||||
public List<AiAlgorithm> queryAll() {
|
||||
return algorithmMapper.queryAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AiAlgorithm> queryActive() {
|
||||
return algorithmMapper.queryActive();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toggleActive(Integer id, Integer isActive) {
|
||||
String now = LocalDateTime.now().format(FORMATTER);
|
||||
algorithmMapper.updateActive(id, isActive, now);
|
||||
configLogService.addLog("ALGORITHM", String.valueOf(id),
|
||||
null, "{\"is_active\":" + isActive + "}", null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void syncFromEdge() {
|
||||
if (!aiServiceConfig.isEnabled()) {
|
||||
log.warn("AI服务未启用,跳过同步");
|
||||
return;
|
||||
}
|
||||
String now = LocalDateTime.now().format(FORMATTER);
|
||||
try {
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
String url = aiServiceConfig.getUrl() + "/api/algorithms";
|
||||
List<Map<String, Object>> remoteAlgos = restTemplate.getForObject(url, List.class);
|
||||
if (remoteAlgos == null || remoteAlgos.isEmpty()) {
|
||||
log.info("边缘端无算法数据");
|
||||
return;
|
||||
}
|
||||
for (Map<String, Object> remote : remoteAlgos) {
|
||||
String algoCode = (String) remote.get("algo_code");
|
||||
AiAlgorithm existing = algorithmMapper.queryByCode(algoCode);
|
||||
if (existing != null) {
|
||||
existing.setAlgoName((String) remote.get("algo_name"));
|
||||
existing.setTargetClass((String) remote.get("target_class"));
|
||||
existing.setParamSchema(remote.get("param_schema") != null ? remote.get("param_schema").toString() : null);
|
||||
existing.setDescription((String) remote.get("description"));
|
||||
existing.setSyncTime(now);
|
||||
existing.setUpdateTime(now);
|
||||
algorithmMapper.updateByCode(existing);
|
||||
} else {
|
||||
AiAlgorithm algo = new AiAlgorithm();
|
||||
algo.setAlgoCode(algoCode);
|
||||
algo.setAlgoName((String) remote.get("algo_name"));
|
||||
algo.setTargetClass((String) remote.get("target_class"));
|
||||
algo.setParamSchema(remote.get("param_schema") != null ? remote.get("param_schema").toString() : null);
|
||||
algo.setDescription((String) remote.get("description"));
|
||||
algo.setIsActive(1);
|
||||
algo.setSyncTime(now);
|
||||
algo.setCreateTime(now);
|
||||
algo.setUpdateTime(now);
|
||||
algorithmMapper.add(algo);
|
||||
}
|
||||
}
|
||||
log.info("从边缘端同步算法完成,共{}个", remoteAlgos.size());
|
||||
} catch (Exception e) {
|
||||
log.error("从边缘端同步算法失败", e);
|
||||
throw new RuntimeException("同步失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.genersoft.iot.vmp.aiot.service.impl;
|
||||
|
||||
import com.genersoft.iot.vmp.aiot.bean.AiConfigLog;
|
||||
import com.genersoft.iot.vmp.aiot.dao.AiConfigLogMapper;
|
||||
import com.genersoft.iot.vmp.aiot.service.IAiConfigLogService;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class AiConfigLogServiceImpl implements IAiConfigLogService {
|
||||
|
||||
@Autowired
|
||||
private AiConfigLogMapper configLogMapper;
|
||||
|
||||
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
@Override
|
||||
public void addLog(String configType, String configId, String oldValue, String newValue, String updatedBy) {
|
||||
AiConfigLog logEntry = new AiConfigLog();
|
||||
logEntry.setConfigType(configType);
|
||||
logEntry.setConfigId(configId);
|
||||
logEntry.setOldValue(oldValue);
|
||||
logEntry.setNewValue(newValue);
|
||||
logEntry.setUpdatedBy(updatedBy);
|
||||
logEntry.setUpdatedAt(LocalDateTime.now().format(FORMATTER));
|
||||
configLogMapper.add(logEntry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageInfo<AiConfigLog> queryList(String configType, String configId, int page, int count) {
|
||||
PageHelper.startPage(page, count);
|
||||
List<AiConfigLog> list = configLogMapper.queryList(configType, configId);
|
||||
return new PageInfo<>(list);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package com.genersoft.iot.vmp.aiot.service.impl;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.genersoft.iot.vmp.aiot.bean.AiAlgorithm;
|
||||
import com.genersoft.iot.vmp.aiot.bean.AiRoi;
|
||||
import com.genersoft.iot.vmp.aiot.bean.AiRoiAlgoBind;
|
||||
import com.genersoft.iot.vmp.aiot.config.AiServiceConfig;
|
||||
import com.genersoft.iot.vmp.aiot.dao.AiAlgorithmMapper;
|
||||
import com.genersoft.iot.vmp.aiot.dao.AiRoiAlgoBindMapper;
|
||||
import com.genersoft.iot.vmp.aiot.dao.AiRoiMapper;
|
||||
import com.genersoft.iot.vmp.aiot.service.IAiConfigService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class AiConfigServiceImpl implements IAiConfigService {
|
||||
|
||||
@Autowired
|
||||
private AiRoiMapper roiMapper;
|
||||
|
||||
@Autowired
|
||||
private AiRoiAlgoBindMapper bindMapper;
|
||||
|
||||
@Autowired
|
||||
private AiAlgorithmMapper algorithmMapper;
|
||||
|
||||
@Autowired
|
||||
private AiServiceConfig aiServiceConfig;
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
@Override
|
||||
public Map<String, Object> exportConfig(String cameraId) {
|
||||
Map<String, Object> config = new LinkedHashMap<>();
|
||||
config.put("camera_id", cameraId);
|
||||
|
||||
List<AiRoi> rois = roiMapper.queryByCameraId(cameraId);
|
||||
List<Map<String, Object>> roiList = new ArrayList<>();
|
||||
for (AiRoi roi : rois) {
|
||||
Map<String, Object> roiMap = new LinkedHashMap<>();
|
||||
roiMap.put("roi_id", roi.getRoiId());
|
||||
roiMap.put("roi_type", roi.getRoiType());
|
||||
roiMap.put("name", roi.getName());
|
||||
roiMap.put("enabled", roi.getEnabled() == 1);
|
||||
|
||||
try {
|
||||
roiMap.put("coordinates", objectMapper.readValue(roi.getCoordinates(), Object.class));
|
||||
} catch (Exception e) {
|
||||
roiMap.put("coordinates", roi.getCoordinates());
|
||||
}
|
||||
|
||||
List<AiRoiAlgoBind> binds = bindMapper.queryByRoiId(roi.getRoiId());
|
||||
List<Map<String, Object>> algoList = new ArrayList<>();
|
||||
for (AiRoiAlgoBind bind : binds) {
|
||||
Map<String, Object> algoMap = new LinkedHashMap<>();
|
||||
algoMap.put("algo_code", bind.getAlgoCode());
|
||||
algoMap.put("enabled", bind.getEnabled() == 1);
|
||||
try {
|
||||
algoMap.put("params", objectMapper.readValue(bind.getParams(), Object.class));
|
||||
} catch (Exception e) {
|
||||
algoMap.put("params", bind.getParams());
|
||||
}
|
||||
algoList.add(algoMap);
|
||||
}
|
||||
roiMap.put("algorithms", algoList);
|
||||
roiList.add(roiMap);
|
||||
}
|
||||
config.put("rois", roiList);
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pushConfig(String cameraId) {
|
||||
if (!aiServiceConfig.isEnabled()) {
|
||||
log.warn("AI服务未启用,跳过推送");
|
||||
throw new RuntimeException("AI服务未启用,请在配置中设置ai.service.enabled=true");
|
||||
}
|
||||
Map<String, Object> config = exportConfig(cameraId);
|
||||
try {
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
String url = aiServiceConfig.getUrl() + "/api/config/receive";
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
HttpEntity<Map<String, Object>> entity = new HttpEntity<>(config, headers);
|
||||
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
|
||||
if (!response.getStatusCode().is2xxSuccessful()) {
|
||||
throw new RuntimeException("推送失败,边缘端返回: " + response.getStatusCode());
|
||||
}
|
||||
log.info("配置推送成功: cameraId={}", cameraId);
|
||||
} catch (Exception e) {
|
||||
log.error("配置推送失败: cameraId={}", cameraId, e);
|
||||
throw new RuntimeException("推送失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
package com.genersoft.iot.vmp.aiot.service.impl;
|
||||
|
||||
import com.genersoft.iot.vmp.aiot.bean.*;
|
||||
import com.genersoft.iot.vmp.aiot.dao.AiAlgorithmMapper;
|
||||
import com.genersoft.iot.vmp.aiot.dao.AiRoiAlgoBindMapper;
|
||||
import com.genersoft.iot.vmp.aiot.dao.AiRoiMapper;
|
||||
import com.genersoft.iot.vmp.aiot.service.IAiConfigLogService;
|
||||
import com.genersoft.iot.vmp.aiot.service.IAiRoiService;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class AiRoiServiceImpl implements IAiRoiService {
|
||||
|
||||
@Autowired
|
||||
private AiRoiMapper roiMapper;
|
||||
|
||||
@Autowired
|
||||
private AiRoiAlgoBindMapper bindMapper;
|
||||
|
||||
@Autowired
|
||||
private AiAlgorithmMapper algorithmMapper;
|
||||
|
||||
@Autowired
|
||||
private IAiConfigLogService configLogService;
|
||||
|
||||
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void save(AiRoi roi) {
|
||||
String now = LocalDateTime.now().format(FORMATTER);
|
||||
roi.setUpdateTime(now);
|
||||
if (roi.getId() != null && roi.getId() > 0) {
|
||||
AiRoi old = roiMapper.queryById(roi.getId());
|
||||
roiMapper.update(roi);
|
||||
configLogService.addLog("ROI", roi.getRoiId(), toJson(old), toJson(roi), null);
|
||||
} else {
|
||||
if (ObjectUtils.isEmpty(roi.getRoiId())) {
|
||||
roi.setRoiId(UUID.randomUUID().toString());
|
||||
}
|
||||
roi.setCreateTime(now);
|
||||
if (roi.getEnabled() == null) {
|
||||
roi.setEnabled(1);
|
||||
}
|
||||
if (roi.getPriority() == null) {
|
||||
roi.setPriority(0);
|
||||
}
|
||||
roiMapper.add(roi);
|
||||
configLogService.addLog("ROI", roi.getRoiId(), null, toJson(roi), null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void delete(String roiId) {
|
||||
AiRoi old = roiMapper.queryByRoiId(roiId);
|
||||
if (old != null) {
|
||||
bindMapper.deleteByRoiId(roiId);
|
||||
roiMapper.deleteByRoiId(roiId);
|
||||
configLogService.addLog("ROI", roiId, toJson(old), null, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AiRoiDetail queryDetail(Integer id) {
|
||||
AiRoi roi = roiMapper.queryById(id);
|
||||
if (roi == null) {
|
||||
return null;
|
||||
}
|
||||
AiRoiDetail detail = new AiRoiDetail();
|
||||
detail.setRoi(roi);
|
||||
|
||||
List<AiRoiAlgoBind> binds = bindMapper.queryByRoiId(roi.getRoiId());
|
||||
List<AiRoiDetail.AiRoiAlgoBindDetail> algoDetails = new ArrayList<>();
|
||||
for (AiRoiAlgoBind bind : binds) {
|
||||
AiRoiDetail.AiRoiAlgoBindDetail ad = new AiRoiDetail.AiRoiAlgoBindDetail();
|
||||
ad.setBind(bind);
|
||||
AiAlgorithm algo = algorithmMapper.queryByCode(bind.getAlgoCode());
|
||||
ad.setAlgorithm(algo);
|
||||
algoDetails.add(ad);
|
||||
}
|
||||
detail.setAlgorithms(algoDetails);
|
||||
return detail;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AiRoi> queryByCameraId(String cameraId) {
|
||||
return roiMapper.queryByCameraId(cameraId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageInfo<AiRoi> queryList(String cameraId, String deviceId, String query, int page, int count) {
|
||||
PageHelper.startPage(page, count);
|
||||
List<AiRoi> list = roiMapper.queryList(cameraId, deviceId, query);
|
||||
return new PageInfo<>(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void bindAlgo(AiRoiAlgoBind bind) {
|
||||
String now = LocalDateTime.now().format(FORMATTER);
|
||||
AiRoiAlgoBind existing = bindMapper.queryByRoiIdAndAlgoCode(bind.getRoiId(), bind.getAlgoCode());
|
||||
if (existing != null) {
|
||||
throw new IllegalArgumentException("该ROI已绑定此算法");
|
||||
}
|
||||
if (ObjectUtils.isEmpty(bind.getBindId())) {
|
||||
bind.setBindId(UUID.randomUUID().toString());
|
||||
}
|
||||
if (bind.getEnabled() == null) {
|
||||
bind.setEnabled(1);
|
||||
}
|
||||
if (bind.getPriority() == null) {
|
||||
bind.setPriority(0);
|
||||
}
|
||||
bind.setCreateTime(now);
|
||||
bind.setUpdateTime(now);
|
||||
bindMapper.add(bind);
|
||||
configLogService.addLog("BIND", bind.getBindId(), null, toJson(bind), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void unbindAlgo(String bindId) {
|
||||
AiRoiAlgoBind old = bindMapper.queryByBindId(bindId);
|
||||
if (old != null) {
|
||||
bindMapper.deleteByBindId(bindId);
|
||||
configLogService.addLog("BIND", bindId, toJson(old), null, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void updateAlgoParams(AiRoiAlgoBind bind) {
|
||||
String now = LocalDateTime.now().format(FORMATTER);
|
||||
AiRoiAlgoBind old = bindMapper.queryByBindId(bind.getBindId());
|
||||
if (old == null) {
|
||||
throw new IllegalArgumentException("绑定关系不存在");
|
||||
}
|
||||
bind.setUpdateTime(now);
|
||||
bindMapper.updateByBindId(bind);
|
||||
configLogService.addLog("BIND", bind.getBindId(), toJson(old), toJson(bind), null);
|
||||
}
|
||||
|
||||
private String toJson(Object obj) {
|
||||
if (obj == null) return null;
|
||||
try {
|
||||
com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();
|
||||
return mapper.writeValueAsString(obj);
|
||||
} catch (Exception e) {
|
||||
log.warn("序列化失败", e);
|
||||
return obj.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,6 +87,8 @@ media:
|
||||
http-port: 6080
|
||||
# [必选选] zlm服务器的hook.admin_params=secret
|
||||
secret: su6TiedN2rVAmBbIDX0aa0QTiBJLBdcf
|
||||
# [重要] ZLM在Docker内运行时,hook回调需用host.docker.internal才能访问宿主机
|
||||
hook-ip: host.docker.internal
|
||||
# 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分, 点播超时建议使用多端口测试
|
||||
rtp:
|
||||
# [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输
|
||||
@@ -110,3 +112,13 @@ user-settings:
|
||||
# 是否返回Date属性,true:不返回,避免摄像头通过该参数自动校时,false:返回,摄像头可能会根据该时间校时
|
||||
disable-date-header: false
|
||||
|
||||
# AI边缘端服务配置
|
||||
ai:
|
||||
service:
|
||||
# FastAPI边缘端地址
|
||||
url: http://localhost:8090
|
||||
# 推送超时ms
|
||||
push-timeout: 10000
|
||||
# 暂未对接时设为false
|
||||
enabled: false
|
||||
|
||||
|
||||
@@ -77,3 +77,10 @@ user-settings:
|
||||
# 推流直播是否录制
|
||||
record-push-live: true
|
||||
auto-apply-play: true
|
||||
|
||||
# AI边缘端服务配置
|
||||
ai:
|
||||
service:
|
||||
url: ${AI_SERVICE_URL:http://localhost:8090}
|
||||
push-timeout: ${AI_PUSH_TIMEOUT:10000}
|
||||
enabled: ${AI_SERVICE_ENABLED:false}
|
||||
|
||||
Reference in New Issue
Block a user