diff --git a/admin/src/main/java/com/multictrl/common/constant/BusinessConstant.java b/admin/src/main/java/com/multictrl/common/constant/BusinessConstant.java index 3016eb3..db05289 100644 --- a/admin/src/main/java/com/multictrl/common/constant/BusinessConstant.java +++ b/admin/src/main/java/com/multictrl/common/constant/BusinessConstant.java @@ -8,6 +8,7 @@ package com.multictrl.common.constant; */ public interface BusinessConstant { + String ZHIMOU_AI_CALLBACK_TOPIC = "thing/product/zhimou_ai_callback"; String WEB_EVENT_TOPIC = "thing/product/%s/web_event"; String NOFLY_ZONE_METHOD = "nofly_zone"; @@ -90,6 +91,7 @@ public interface BusinessConstant { String DOCK_NOFLY_ZONE = "dock_nofly_zone_"; String DOCK_NOFLY_ZONE_TRIGGER_SIGN = "dock_nofly_zone_trigger_sign_"; String ZHIMOU_TOKEN = "zhiMou_token"; + String DOCK_ZHIMOU_TASK_ID = "dock_zhiMou_task_id_"; //********************************* other *********************************// String HTTP_PROTOCOL = "http://"; diff --git a/admin/src/main/java/com/multictrl/common/constant/SysParamsKey.java b/admin/src/main/java/com/multictrl/common/constant/SysParamsKey.java index 65d802a..0a98330 100644 --- a/admin/src/main/java/com/multictrl/common/constant/SysParamsKey.java +++ b/admin/src/main/java/com/multictrl/common/constant/SysParamsKey.java @@ -9,7 +9,11 @@ package com.multictrl.common.constant; public interface SysParamsKey { //********************************* zhimou *********************************// - String ZHIMOU_AI_URL = "zhi_mou_ai_url"; - String ZHIMOU_AI_APP_KEY = "zhi_mou_ai_app_key"; - String ZHIMOU_AI_APP_SECRET = "zhi_mou_ai_app_secret"; + String ZHIMOU_AI_URL = "zhimou_ai_url"; + String ZHIMOU_AI_APP_KEY = "zhimou_ai_app_key"; + 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"; } diff --git a/admin/src/main/java/com/multictrl/common/utils/Utils.java b/admin/src/main/java/com/multictrl/common/utils/Utils.java index f3d2529..cd10fe3 100644 --- a/admin/src/main/java/com/multictrl/common/utils/Utils.java +++ b/admin/src/main/java/com/multictrl/common/utils/Utils.java @@ -5,6 +5,8 @@ import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONArray; import cn.hutool.json.JSONObject; 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.JpegProcessingException; import com.drew.imaging.mp4.Mp4MetadataReader; @@ -75,6 +77,15 @@ public class Utils { 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" diff --git a/admin/src/main/java/com/multictrl/modules/business/controller/ZhiMouController.java b/admin/src/main/java/com/multictrl/modules/business/controller/ZhiMouController.java index 20391fe..451478f 100644 --- a/admin/src/main/java/com/multictrl/modules/business/controller/ZhiMouController.java +++ b/admin/src/main/java/com/multictrl/modules/business/controller/ZhiMouController.java @@ -2,15 +2,16 @@ package com.multictrl.modules.business.controller; import com.alibaba.fastjson2.JSONObject; import com.multictrl.common.annotation.ApiOrder; +import com.multictrl.common.annotation.LogOperation; 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 io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import org.apache.shiro.authz.annotation.RequiresPermissions; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; /** * 智眸AI接口封装 @@ -33,4 +34,23 @@ public class ZhiMouController { JSONObject data = zhiMouService.getApp(); return zhiMouService.resultHandle(data); } + + @Operation(summary = "开始AI检测任务") + @LogOperation("开始AI检测任务") + @PostMapping("/startByWeb") + @RequiresPermissions("bus:zhimou:startByWeb") + public Result 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 stopByWeb(@RequestParam String dockSn) { + JSONObject data = zhiMouService.stopByWeb(dockSn); + return zhiMouService.resultHandle(data); + } } diff --git a/admin/src/main/java/com/multictrl/modules/business/dto/zhimou/ZhiMouAiStart.java b/admin/src/main/java/com/multictrl/modules/business/dto/zhimou/ZhiMouAiStart.java new file mode 100644 index 0000000..5d5fa1d --- /dev/null +++ b/admin/src/main/java/com/multictrl/modules/business/dto/zhimou/ZhiMouAiStart.java @@ -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 rules; +} diff --git a/admin/src/main/java/com/multictrl/modules/business/dto/zhimou/ZhiMouTask.java b/admin/src/main/java/com/multictrl/modules/business/dto/zhimou/ZhiMouTask.java index 29ab7a5..b8c46fd 100644 --- a/admin/src/main/java/com/multictrl/modules/business/dto/zhimou/ZhiMouTask.java +++ b/admin/src/main/java/com/multictrl/modules/business/dto/zhimou/ZhiMouTask.java @@ -17,7 +17,7 @@ public class ZhiMouTask { * 应用唯一ID */ @JsonProperty("app_id") - private String appID; + private String appId; /** * 输出视频比特率 */ @@ -26,7 +26,7 @@ public class ZhiMouTask { * 三方业务ID */ @JsonProperty("business_id") - private String businessID; + private String businessId; /** * 设备号 */ diff --git a/admin/src/main/java/com/multictrl/modules/business/service/SrsService.java b/admin/src/main/java/com/multictrl/modules/business/service/SrsService.java index 56919dc..88f51f0 100644 --- a/admin/src/main/java/com/multictrl/modules/business/service/SrsService.java +++ b/admin/src/main/java/com/multictrl/modules/business/service/SrsService.java @@ -28,6 +28,9 @@ public interface SrsService { //获取飞机推流地址 String getUavPushLiveUrl(String dockSn); + //获取飞机AI推流地址 + String getUavAiPushLiveUrl(String dockSn); + //获取dao SrsRecordDao getDao(); } diff --git a/admin/src/main/java/com/multictrl/modules/business/service/ZhiMouService.java b/admin/src/main/java/com/multictrl/modules/business/service/ZhiMouService.java index b2d745b..cc4daf5 100644 --- a/admin/src/main/java/com/multictrl/modules/business/service/ZhiMouService.java +++ b/admin/src/main/java/com/multictrl/modules/business/service/ZhiMouService.java @@ -2,6 +2,7 @@ package com.multictrl.modules.business.service; import com.alibaba.fastjson2.JSONObject; import com.multictrl.common.utils.Result; +import com.multictrl.modules.business.dto.zhimou.ZhiMouAiStart; import com.multictrl.modules.business.dto.zhimou.ZhiMouInfo; /** @@ -20,4 +21,10 @@ public interface ZhiMouService { //获取应用列表 JSONObject getApp(); + + //开始AI检测任务 + JSONObject startByWeb(ZhiMouAiStart zhiMouAiStart); + + //停止AI检测任务 + JSONObject stopByWeb(String dockSn); } diff --git a/admin/src/main/java/com/multictrl/modules/business/service/impl/SrsServiceImpl.java b/admin/src/main/java/com/multictrl/modules/business/service/impl/SrsServiceImpl.java index 14f67a4..7b457d9 100644 --- a/admin/src/main/java/com/multictrl/modules/business/service/impl/SrsServiceImpl.java +++ b/admin/src/main/java/com/multictrl/modules/business/service/impl/SrsServiceImpl.java @@ -145,6 +145,12 @@ public class SrsServiceImpl implements SrsService { 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 public SrsRecordDao getDao() { return srsRecordDao; diff --git a/admin/src/main/java/com/multictrl/modules/business/service/impl/ZhiMouServiceImpl.java b/admin/src/main/java/com/multictrl/modules/business/service/impl/ZhiMouServiceImpl.java index 0a9196e..10ba3a1 100644 --- a/admin/src/main/java/com/multictrl/modules/business/service/impl/ZhiMouServiceImpl.java +++ b/admin/src/main/java/com/multictrl/modules/business/service/impl/ZhiMouServiceImpl.java @@ -1,18 +1,23 @@ package com.multictrl.modules.business.service.impl; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.http.HttpUtil; +import cn.hutool.http.Method; import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONObject; 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.SysParamsKey; import com.multictrl.common.exception.ErrorCode; import com.multictrl.common.exception.RenException; import com.multictrl.common.utils.CacheUtils; 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.sys.dao.SysParamsDao; import com.multictrl.modules.sys.entity.SysParamsEntity; @@ -24,6 +29,8 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * 智眸AI接口封装 @@ -36,6 +43,8 @@ import java.util.Map; @RequiredArgsConstructor public class ZhiMouServiceImpl implements ZhiMouService { private final SysParamsDao paramsDao; + private final SrsService srsService; + private final MqttConfig mqttConfig; private static final String APP_KEY = "appkey"; private static final String APP_SECRET = "appsecret"; 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); } @@ -181,4 +191,105 @@ public class ZhiMouServiceImpl implements ZhiMouService { } 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() + .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() + .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; + } + } } diff --git a/common/src/main/java/com/multictrl/common/exception/ErrorCode.java b/common/src/main/java/com/multictrl/common/exception/ErrorCode.java index 6e6e52b..df44f9a 100644 --- a/common/src/main/java/com/multictrl/common/exception/ErrorCode.java +++ b/common/src/main/java/com/multictrl/common/exception/ErrorCode.java @@ -71,4 +71,5 @@ public interface ErrorCode { int DELETE_FAIL = 20030; int ZHIMOU_NO_CONFIG = 20031; int ZHIMOU_REQUEST_ERROR = 20032; + int ZHIMOU_TASK_NOT_EXIST = 20033; } diff --git a/common/src/main/resources/i18n/messages.properties b/common/src/main/resources/i18n/messages.properties index c47e875..00bdf9f 100644 --- a/common/src/main/resources/i18n/messages.properties +++ b/common/src/main/resources/i18n/messages.properties @@ -59,4 +59,5 @@ 20029=\u8bf7\u4e0a\u4f20{0}\u683c\u5f0f\u6587\u4ef6 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 -20032=AI\u56FE\u50CF\u68C0\u6D4B\u7B97\u6CD5\u8BF7\u6C42\u51FA\u9519 \ No newline at end of file +20032=AI\u56FE\u50CF\u68C0\u6D4B\u7B97\u6CD5\u8BF7\u6C42\u51FA\u9519 +20033=AI\u4EFB\u52A1\u672A\u5F00\u542F \ No newline at end of file diff --git a/prj-deploy/file/api/conf/application-docker.yml b/prj-deploy/file/api/conf/application-docker.yml index 43573e5..6a69fba 100644 --- a/prj-deploy/file/api/conf/application-docker.yml +++ b/prj-deploy/file/api/conf/application-docker.yml @@ -41,7 +41,7 @@ minio: mqtt: client1: - url: tcp://dj-multictrl-emqx:1883 + url: tcp://${host.ip}:61627 username: dock password: Dock@2023 subClientId: dj-one-sub