增加智眸AI接口封装

This commit is contained in:
sdy 2026-06-10 17:47:16 +08:00
parent ab47d75ca3
commit 55273df8d7
13 changed files with 205 additions and 11 deletions

View File

@ -8,6 +8,7 @@ package com.multictrl.common.constant;
*/ */
public interface BusinessConstant { public interface BusinessConstant {
String ZHIMOU_AI_CALLBACK_TOPIC = "thing/product/zhimou_ai_callback";
String WEB_EVENT_TOPIC = "thing/product/%s/web_event"; String WEB_EVENT_TOPIC = "thing/product/%s/web_event";
String NOFLY_ZONE_METHOD = "nofly_zone"; String NOFLY_ZONE_METHOD = "nofly_zone";
@ -90,6 +91,7 @@ public interface BusinessConstant {
String DOCK_NOFLY_ZONE = "dock_nofly_zone_"; String DOCK_NOFLY_ZONE = "dock_nofly_zone_";
String DOCK_NOFLY_ZONE_TRIGGER_SIGN = "dock_nofly_zone_trigger_sign_"; String DOCK_NOFLY_ZONE_TRIGGER_SIGN = "dock_nofly_zone_trigger_sign_";
String ZHIMOU_TOKEN = "zhiMou_token"; String ZHIMOU_TOKEN = "zhiMou_token";
String DOCK_ZHIMOU_TASK_ID = "dock_zhiMou_task_id_";
//********************************* other *********************************// //********************************* other *********************************//
String HTTP_PROTOCOL = "http://"; String HTTP_PROTOCOL = "http://";

View File

@ -9,7 +9,11 @@ package com.multictrl.common.constant;
public interface SysParamsKey { public interface SysParamsKey {
//********************************* zhimou *********************************// //********************************* zhimou *********************************//
String ZHIMOU_AI_URL = "zhi_mou_ai_url"; String ZHIMOU_AI_URL = "zhimou_ai_url";
String ZHIMOU_AI_APP_KEY = "zhi_mou_ai_app_key"; String ZHIMOU_AI_APP_KEY = "zhimou_ai_app_key";
String ZHIMOU_AI_APP_SECRET = "zhi_mou_ai_app_secret"; String ZHIMOU_AI_APP_SECRET = "zhimou_ai_app_secret";
//********************************* sys component *********************************//
String SRS_RTMP_MAP_URL = "srs_rtmp_map_url";
String EMQX_MAP_URL = "emqx_map_url";
} }

View File

@ -5,6 +5,8 @@ import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray; import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject; import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.filter.NameFilter;
import com.drew.imaging.jpeg.JpegMetadataReader; import com.drew.imaging.jpeg.JpegMetadataReader;
import com.drew.imaging.jpeg.JpegProcessingException; import com.drew.imaging.jpeg.JpegProcessingException;
import com.drew.imaging.mp4.Mp4MetadataReader; import com.drew.imaging.mp4.Mp4MetadataReader;
@ -75,6 +77,15 @@ public class Utils {
return snakeJson; return snakeJson;
} }
/*
* 阿里FastJson2驼峰转下划线
*/
public static com.alibaba.fastjson2.JSONObject beanToSnakeJsonFastJson(Object o) {
NameFilter snakeCaseFilter = (object, name, value)
-> name.replaceAll("([a-z])([A-Z])", "$1_$2").toLowerCase();
return com.alibaba.fastjson2.JSONObject.parseObject(JSON.toJSONString(o, snakeCaseFilter));
}
/** /**
* 获取最后两个字符 * 获取最后两个字符
* "a/b/c/d" "c/d" * "a/b/c/d" "c/d"

View File

@ -2,15 +2,16 @@ package com.multictrl.modules.business.controller;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import com.multictrl.common.annotation.ApiOrder; import com.multictrl.common.annotation.ApiOrder;
import com.multictrl.common.annotation.LogOperation;
import com.multictrl.common.utils.Result; import com.multictrl.common.utils.Result;
import com.multictrl.common.validator.ValidatorUtils;
import com.multictrl.modules.business.dto.zhimou.ZhiMouAiStart;
import com.multictrl.modules.business.service.ZhiMouService; import com.multictrl.modules.business.service.ZhiMouService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.shiro.authz.annotation.RequiresPermissions; import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/** /**
* 智眸AI接口封装 * 智眸AI接口封装
@ -33,4 +34,23 @@ public class ZhiMouController {
JSONObject data = zhiMouService.getApp(); JSONObject data = zhiMouService.getApp();
return zhiMouService.resultHandle(data); return zhiMouService.resultHandle(data);
} }
@Operation(summary = "开始AI检测任务")
@LogOperation("开始AI检测任务")
@PostMapping("/startByWeb")
@RequiresPermissions("bus:zhimou:startByWeb")
public Result<Object> startByWeb(@RequestBody ZhiMouAiStart zhiMouAiStart) {
ValidatorUtils.validateEntity(zhiMouAiStart);
JSONObject data = zhiMouService.startByWeb(zhiMouAiStart);
return zhiMouService.resultHandle(data);
}
@Operation(summary = "停止AI检测任务")
@LogOperation("停止AI检测任务")
@PostMapping("/stopByWeb")
@RequiresPermissions("bus:zhimou:stopByWeb")
public Result<Object> stopByWeb(@RequestParam String dockSn) {
JSONObject data = zhiMouService.stopByWeb(dockSn);
return zhiMouService.resultHandle(data);
}
} }

View File

@ -0,0 +1,28 @@
package com.multictrl.modules.business.dto.zhimou;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import java.util.List;
/**
* 开始AI检测参数
*
* @author Sdy
* @since 1.0.0 2026/6/10
*/
@Schema(name = "开始AI检测参数")
@Data
public class ZhiMouAiStart {
@NotBlank(message = "设备编号不能为空")
@Schema(description = "设备编号")
private String dockSn;
@NotBlank(message = "应用编号不能为空")
@Schema(description = "应用编号")
private String appId;
@Schema(description = "指定开启应用下规则列表,不指定则开启所有规则")
private List<String> rules;
}

View File

@ -17,7 +17,7 @@ public class ZhiMouTask {
* 应用唯一ID * 应用唯一ID
*/ */
@JsonProperty("app_id") @JsonProperty("app_id")
private String appID; private String appId;
/** /**
* 输出视频比特率 * 输出视频比特率
*/ */
@ -26,7 +26,7 @@ public class ZhiMouTask {
* 三方业务ID * 三方业务ID
*/ */
@JsonProperty("business_id") @JsonProperty("business_id")
private String businessID; private String businessId;
/** /**
* 设备号 * 设备号
*/ */

View File

@ -28,6 +28,9 @@ public interface SrsService {
//获取飞机推流地址 //获取飞机推流地址
String getUavPushLiveUrl(String dockSn); String getUavPushLiveUrl(String dockSn);
//获取飞机AI推流地址
String getUavAiPushLiveUrl(String dockSn);
//获取dao //获取dao
SrsRecordDao getDao(); SrsRecordDao getDao();
} }

View File

@ -2,6 +2,7 @@ package com.multictrl.modules.business.service;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import com.multictrl.common.utils.Result; import com.multictrl.common.utils.Result;
import com.multictrl.modules.business.dto.zhimou.ZhiMouAiStart;
import com.multictrl.modules.business.dto.zhimou.ZhiMouInfo; import com.multictrl.modules.business.dto.zhimou.ZhiMouInfo;
/** /**
@ -20,4 +21,10 @@ public interface ZhiMouService {
//获取应用列表 //获取应用列表
JSONObject getApp(); JSONObject getApp();
//开始AI检测任务
JSONObject startByWeb(ZhiMouAiStart zhiMouAiStart);
//停止AI检测任务
JSONObject stopByWeb(String dockSn);
} }

View File

@ -145,6 +145,12 @@ public class SrsServiceImpl implements SrsService {
return RTMP_PROTOCOL + srsConfig.getIp() + ":" + srsConfig.getRtmpPort() + "/" + dockSn + "/uav?token=" + srsConfig.getRtmpToken(); return RTMP_PROTOCOL + srsConfig.getIp() + ":" + srsConfig.getRtmpPort() + "/" + dockSn + "/uav?token=" + srsConfig.getRtmpToken();
} }
@Override
public String getUavAiPushLiveUrl(String dockSn) {
return RTMP_PROTOCOL + srsConfig.getIp() + ":" + srsConfig.getRtmpPort() + "/" + dockSn + "/uav_ai?token=" + srsConfig.getRtmpToken();
}
@Override @Override
public SrsRecordDao getDao() { public SrsRecordDao getDao() {
return srsRecordDao; return srsRecordDao;

View File

@ -1,18 +1,23 @@
package com.multictrl.modules.business.service.impl; package com.multictrl.modules.business.service.impl;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil; import cn.hutool.http.HttpUtil;
import cn.hutool.http.Method;
import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.multictrl.common.config.MqttConfig;
import com.multictrl.common.constant.BusinessConstant; import com.multictrl.common.constant.BusinessConstant;
import com.multictrl.common.constant.SysParamsKey; import com.multictrl.common.constant.SysParamsKey;
import com.multictrl.common.exception.ErrorCode; import com.multictrl.common.exception.ErrorCode;
import com.multictrl.common.exception.RenException; import com.multictrl.common.exception.RenException;
import com.multictrl.common.utils.CacheUtils; import com.multictrl.common.utils.CacheUtils;
import com.multictrl.common.utils.Result; import com.multictrl.common.utils.Result;
import com.multictrl.modules.business.dto.zhimou.ZhiMouInfo; import com.multictrl.common.utils.Utils;
import com.multictrl.modules.business.dto.zhimou.*;
import com.multictrl.modules.business.service.SrsService;
import com.multictrl.modules.business.service.ZhiMouService; import com.multictrl.modules.business.service.ZhiMouService;
import com.multictrl.modules.sys.dao.SysParamsDao; import com.multictrl.modules.sys.dao.SysParamsDao;
import com.multictrl.modules.sys.entity.SysParamsEntity; import com.multictrl.modules.sys.entity.SysParamsEntity;
@ -24,6 +29,8 @@ import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** /**
* 智眸AI接口封装 * 智眸AI接口封装
@ -36,6 +43,8 @@ import java.util.Map;
@RequiredArgsConstructor @RequiredArgsConstructor
public class ZhiMouServiceImpl implements ZhiMouService { public class ZhiMouServiceImpl implements ZhiMouService {
private final SysParamsDao paramsDao; private final SysParamsDao paramsDao;
private final SrsService srsService;
private final MqttConfig mqttConfig;
private static final String APP_KEY = "appkey"; private static final String APP_KEY = "appkey";
private static final String APP_SECRET = "appsecret"; private static final String APP_SECRET = "appsecret";
private static final String AUTHORIZATION = "Authorization"; private static final String AUTHORIZATION = "Authorization";
@ -95,6 +104,7 @@ public class ZhiMouServiceImpl implements ZhiMouService {
} }
} }
} }
log.error("invoke zhiMou resultHandle result: {}", result);
return new Result<>().error(ErrorCode.ZHIMOU_REQUEST_ERROR); return new Result<>().error(ErrorCode.ZHIMOU_REQUEST_ERROR);
} }
@ -181,4 +191,105 @@ public class ZhiMouServiceImpl implements ZhiMouService {
} }
return null; return null;
} }
@Override
public JSONObject startByWeb(ZhiMouAiStart zhiMouAiStart) {
String dockSn = zhiMouAiStart.getDockSn();
ZhiMouTask zhiMouTask = new ZhiMouTask();
zhiMouTask.setMode("video");
zhiMouTask.setAppId(zhiMouAiStart.getAppId());
zhiMouTask.setDevice(dockSn);
zhiMouTask.setBusinessId((String) CacheUtils.get(BusinessConstant.IN_FLIGHT_WORKING_TASK_ID + dockSn));
zhiMouTask.setRules(zhiMouAiStart.getRules());
zhiMouTask.setInput(srsService.getUavPushLiveUrl(dockSn));
zhiMouTask.setOutput(srsService.getUavAiPushLiveUrl(dockSn));
//内网部署的服务想要使用公网的知眸服务需要把srs-rtmp的1935映射到公网这样知眸才能拉到视频流处理并推流
SysParamsEntity rtmpMapUrl = paramsDao.selectOne(new QueryWrapper<SysParamsEntity>()
.eq("param_code", SysParamsKey.SRS_RTMP_MAP_URL));
if (rtmpMapUrl != null) {
String paramValue = rtmpMapUrl.getParamValue();
if (StrUtil.isNotBlank(paramValue) && paramValue.contains(":")) {
//替换掉input中的ip和端口
zhiMouTask.setInput(replaceRtmpIpPort(zhiMouTask.getInput(), paramValue.split(":")[0],
paramValue.split(":")[1]));
zhiMouTask.setOutput(replaceRtmpIpPort(zhiMouTask.getOutput(), paramValue.split(":")[0],
paramValue.split(":")[1]));
}
}
zhiMouTask.setOsd(new Osd());
Report report = new Report();
ReportMqtt mqtt = new ReportMqtt();
MqttConfig.Client client1 = mqttConfig.getClient1();
String hostUrl = client1.getUrl().replaceAll(BusinessConstant.TCP_PROTOCOL, "");
mqtt.setBrokerHost(hostUrl.split(":")[0]);
mqtt.setBrokerPort(Long.parseLong(hostUrl.split(":")[1]));
//内网部署的服务想要使用公网的知眸服务需要把emqx的1883映射到公网这样知眸才能通知系统检测结果
SysParamsEntity emqxMapUrl = paramsDao.selectOne(new QueryWrapper<SysParamsEntity>()
.eq("param_code", SysParamsKey.EMQX_MAP_URL));
if (emqxMapUrl != null) {
String paramValue = emqxMapUrl.getParamValue();
if (StrUtil.isNotBlank(paramValue) && paramValue.contains(":")) {
mqtt.setBrokerHost(paramValue.split(":")[0]);
mqtt.setBrokerPort(Long.parseLong(paramValue.split(":")[1]));
}
}
mqtt.setSsl(false);
mqtt.setClientID("zhi_mou_ai_" + IdUtil.getSnowflakeNextIdStr());
mqtt.setUsername(client1.getUsername());
mqtt.setPassword(client1.getPassword());
mqtt.setTopic(BusinessConstant.ZHIMOU_AI_CALLBACK_TOPIC);
mqtt.setQos(0L);
report.setMqtt(mqtt);
zhiMouTask.setReport(report);
JSONObject jsonObject = Utils.beanToSnakeJsonFastJson(zhiMouTask);
//执行命令
ZhiMouInfo zhiMouInfo = getToken();
String result = HttpUtil.createPost(zhiMouInfo.getUrl() + TASK)
.body(jsonObject.toJSONString())
.header(AUTHORIZATION, BEARER + zhiMouInfo.getToken())
.execute()
.body();
log.debug("invoke zhiMou start request: {},result: {}", jsonObject, result);
if (StrUtil.isNotBlank(result)) {
JSONObject parse = JSONObject.parse(result);
if (parse.containsKey("code") && parse.getInteger("code") == 0) {
String taskId = parse.getJSONObject("data").getString("task_id");
CacheUtils.set(BusinessConstant.DOCK_ZHIMOU_TASK_ID + dockSn, taskId);
}
return parse;
}
return null;
}
@Override
public JSONObject stopByWeb(String dockSn) {
String taskId = (String) CacheUtils.get(BusinessConstant.DOCK_ZHIMOU_TASK_ID + dockSn);
if (StrUtil.isBlank(taskId)) {
throw new RenException(ErrorCode.ZHIMOU_TASK_NOT_EXIST);
}
ZhiMouInfo zhiMouInfo = getToken();
String result = HttpUtil.createRequest(Method.DELETE, zhiMouInfo.getUrl() + TASK + "/" + taskId)
.header(AUTHORIZATION, BEARER + zhiMouInfo.getToken())
.execute()
.body();
log.debug("invoke zhiMou stop taskId: {},result: {}", taskId, result);
if (StrUtil.isNotBlank(result)) {
return JSONObject.parse(result);
}
return null;
}
private String replaceRtmpIpPort(String originalUrl, String newIp, String newPort) {
// 定义RTMP URL的正则表达式模式
Pattern pattern = Pattern.compile("^rtmp://([^/:]+):([^/]+)(/.*)$");
Matcher matcher = pattern.matcher(originalUrl);
if (matcher.matches()) {
// 构建新的URL
return "rtmp://" + newIp + ":" + newPort + matcher.group(3);
} else {
// 如果URL格式不符合预期返回原始URL或抛出异常
return originalUrl;
}
}
} }

View File

@ -71,4 +71,5 @@ public interface ErrorCode {
int DELETE_FAIL = 20030; int DELETE_FAIL = 20030;
int ZHIMOU_NO_CONFIG = 20031; int ZHIMOU_NO_CONFIG = 20031;
int ZHIMOU_REQUEST_ERROR = 20032; int ZHIMOU_REQUEST_ERROR = 20032;
int ZHIMOU_TASK_NOT_EXIST = 20033;
} }

View File

@ -60,3 +60,4 @@
20030=\u5220\u9664{0}\u5931\u8D25\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458 20030=\u5220\u9664{0}\u5931\u8D25\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458
20031=AI\u56FE\u50CF\u68C0\u6D4B\u7B97\u6CD5\u4FE1\u606F\u672A\u914D\u7F6E 20031=AI\u56FE\u50CF\u68C0\u6D4B\u7B97\u6CD5\u4FE1\u606F\u672A\u914D\u7F6E
20032=AI\u56FE\u50CF\u68C0\u6D4B\u7B97\u6CD5\u8BF7\u6C42\u51FA\u9519 20032=AI\u56FE\u50CF\u68C0\u6D4B\u7B97\u6CD5\u8BF7\u6C42\u51FA\u9519
20033=AI\u4EFB\u52A1\u672A\u5F00\u542F

View File

@ -41,7 +41,7 @@ minio:
mqtt: mqtt:
client1: client1:
url: tcp://dj-multictrl-emqx:1883 url: tcp://${host.ip}:61627
username: dock username: dock
password: Dock@2023 password: Dock@2023
subClientId: dj-one-sub subClientId: dj-one-sub