增加智眸AI接口封装

This commit is contained in:
sdy 2026-06-09 21:12:05 +08:00
parent 61ca367330
commit ab47d75ca3
15 changed files with 585 additions and 1 deletions

View File

@ -89,6 +89,7 @@ public interface BusinessConstant {
//********************************* other cache key *********************************//
String DOCK_NOFLY_ZONE = "dock_nofly_zone_";
String DOCK_NOFLY_ZONE_TRIGGER_SIGN = "dock_nofly_zone_trigger_sign_";
String ZHIMOU_TOKEN = "zhiMou_token";
//********************************* other *********************************//
String HTTP_PROTOCOL = "http://";

View File

@ -0,0 +1,15 @@
package com.multictrl.common.constant;
/**
* 系统参数key
*
* @author Sdy
* @since 1.0.0 2026/6/9
*/
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";
}

View File

@ -0,0 +1,36 @@
package com.multictrl.modules.business.controller;
import com.alibaba.fastjson2.JSONObject;
import com.multictrl.common.annotation.ApiOrder;
import com.multictrl.common.utils.Result;
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;
/**
* 智眸AI接口封装
*
* @author Sdy
* @since 1.0.0 2026/6/9
*/
@RestController
@RequestMapping("business/zhiMou")
@Tag(name = "智眸AI接口")
@ApiOrder(23)
@RequiredArgsConstructor
public class ZhiMouController {
private final ZhiMouService zhiMouService;
@Operation(summary = "获取应用列表")
@GetMapping("/getApp")
@RequiresPermissions("bus:zhimou:getApp")
public Result<Object> getApp() {
JSONObject data = zhiMouService.getApp();
return zhiMouService.resultHandle(data);
}
}

View File

@ -0,0 +1,23 @@
package com.multictrl.modules.business.dto.zhimou;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
* 大疆MQTT配置
*
* @author Sdy
* @since 1.0.0 2026/6/9
*/
@Data
public class Dji {
/**
* 无人机设备码大疆无人机设备SN
*/
@JsonProperty("device_sn")
private String deviceSn;
/**
* MQTT订阅配置
*/
private DjiMqtt mqtt;
}

View File

@ -0,0 +1,26 @@
package com.multictrl.modules.business.dto.zhimou;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
* MQTT配置
*
* @author Sdy
* @since 1.0.0 2026/6/9
*/
@Data
public class DjiMqtt {
@JsonProperty("broker_host")
private String brokerHost;
@JsonProperty("broker_port")
private Long brokerPort;
@JsonProperty("client_id")
private String clientID;
private String password;
private Boolean ssl;
private String username;
}

View File

@ -0,0 +1,33 @@
package com.multictrl.modules.business.dto.zhimou;
import lombok.Data;
/**
* mqtt遗言功能
*
* @author Sdy
* @since 1.0.0 2026/6/9
*/
@Data
public class Lwt {
/**
* 是否开启mqtt遗言功能
*/
private Boolean enable;
/**
* mqtt遗言内容
*/
private String message;
/**
* mqtt遗言qos
*/
private Long qos;
/**
* mqtt遗言retain
*/
private Boolean retain;
/**
* mqtt遗言主题
*/
private String topic;
}

View File

@ -0,0 +1,74 @@
package com.multictrl.modules.business.dto.zhimou;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
/**
* 输出OSD配置
*
* @author Sdy
* @since 1.0.0 2026/6/9
*/
@Data
public class Osd {
/**
* 框体粗细
*/
@JsonProperty("border_size")
private Long borderSize = 0L;
/**
* 是否展示运动方向角方向单位为 角度开发测试用
*/
@JsonProperty("display_angle")
private Boolean displayAngle = true;
/**
* 是否展示目标框
*/
@JsonProperty("display_box")
private Boolean displayBox = true;
/**
* 是否展示右上角计数模块
*/
@JsonProperty("display_count")
private Boolean displayCount = true;
/**
* 是否展示目标遮罩
*/
@JsonProperty("display_mask")
private Boolean displayMask = true;
/**
* 是否展示目标名称
*/
@JsonProperty("display_name")
private Boolean displayName = true;
/**
* 是否展示画面像素速度速度单位为 像素/开发测试用
*/
@JsonProperty("display_speed")
private Boolean displaySpeed = true;
/**
* 是否展示目标追踪ID
*/
@JsonProperty("display_track")
private Boolean displayTrack = true;
/**
* 字体大小
*/
@JsonProperty("font_size")
private Long fontSize = 0L;
/**
* 在输出视频流中不展示的规则列表
*/
@JsonProperty("hide_rule")
private List<String> hideRule;
/**
* 视频画面过滤区域
*/
private List<List<Double>> polygon;
private String deviceId;
private String taskId;
}

View File

@ -0,0 +1,21 @@
package com.multictrl.modules.business.dto.zhimou;
import lombok.Data;
/**
* 数据推送配置
*
* @author Sdy
* @since 1.0.0 2026/6/9
*/
@Data
public class Report {
/**
* http消息回调接口地址
*/
private String httpurl;
/**
* 任务状态数据推送不传时可使用配置文件中配置
*/
private ReportMqtt mqtt;
}

View File

@ -0,0 +1,53 @@
package com.multictrl.modules.business.dto.zhimou;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
* 任务状态数据推送不传时可使用配置文件中配置
*
* @author Sdy
* @since 1.0.0 2026/6/9
*/
@Data
public class ReportMqtt {
/**
* mqtt broker地址
*/
@JsonProperty("broker_host")
private String brokerHost;
/**
* mqtt broker端口默认1883
*/
@JsonProperty("broker_port")
private Long brokerPort;
/**
* mqtt客户端ID
*/
@JsonProperty("client_id")
private String clientID;
/**
* mqtt遗言功能
*/
private Lwt lwt;
/**
* mqtt密码
*/
private String password;
/**
* 发布qos
*/
private Long qos;
/**
* 是否使用ssl连接
*/
private Boolean ssl;
/**
* 发布的主题名
*/
private String topic;
/**
* mqtt用户名
*/
private String username;
}

View File

@ -0,0 +1,16 @@
package com.multictrl.modules.business.dto.zhimou;
import lombok.Data;
/**
* 智眸信息
*
* @author Sdy
* @since 1.0.0 2026/6/9
*/
@Data
public class ZhiMouInfo {
private String url;
private String token;
private Long expire;
}

View File

@ -0,0 +1,75 @@
package com.multictrl.modules.business.dto.zhimou;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
/**
* 算法任务配置
*
* @author Sdy
* @since 1.0.0 2026/6/9
*/
@Data
public class ZhiMouTask {
/**
* 应用唯一ID
*/
@JsonProperty("app_id")
private String appID;
/**
* 输出视频比特率
*/
private Long bitrate;
/**
* 三方业务ID
*/
@JsonProperty("business_id")
private String businessID;
/**
* 设备号
*/
private String device;
/**
* 指定应用下禁用的规则
*/
@JsonProperty("disable_rules")
private List<String> disableRules;
/**
* 大疆MQTT配置
*/
private Dji dji;
/**
* 输出视频高
*/
private Long height;
/**
* 视频任务输入流
*/
private String input;
/**
* 任务类型video | image 默认视频任务
*/
private String mode;
/**
* 输出OSD配置
*/
private Osd osd;
/**
* 视频任务输出流
*/
private String output;
/**
* 数据推送配置
*/
private Report report;
/**
* 指定开启应用下规则列表不指定则开启所有规则
*/
private List<String> rules;
/**
* 输出视频宽
*/
private Long width;
}

View File

@ -0,0 +1,23 @@
package com.multictrl.modules.business.service;
import com.alibaba.fastjson2.JSONObject;
import com.multictrl.common.utils.Result;
import com.multictrl.modules.business.dto.zhimou.ZhiMouInfo;
/**
* 智眸AI接口封装
*
* @author Sdy
* @since 1.0.0 2026/6/9
*/
public interface ZhiMouService {
//返回处理
Result<Object> resultHandle(JSONObject result);
//获取token
ZhiMouInfo getToken();
//获取应用列表
JSONObject getApp();
}

View File

@ -0,0 +1,184 @@
package com.multictrl.modules.business.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
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.modules.business.service.ZhiMouService;
import com.multictrl.modules.sys.dao.SysParamsDao;
import com.multictrl.modules.sys.entity.SysParamsEntity;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 智眸AI接口封装
*
* @author Sdy
* @since 1.0.0 2026/6/9
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class ZhiMouServiceImpl implements ZhiMouService {
private final SysParamsDao paramsDao;
private static final String APP_KEY = "appkey";
private static final String APP_SECRET = "appsecret";
private static final String AUTHORIZATION = "Authorization";
private static final String BEARER = "Bearer ";
private static final String GET_TOKEN = "/api/token";
private static final String GET_APP = "/api/app";
private static final String TASK = "/api/task";
private static final String MESSAGE_RECORD = "/api/message_record";
private static final String MESSAGE_RECORD_COUNT = "/api/task_record/%s/message_trace";
private static final String REPORT = "/api/task/%s/report";
private static final String TASK_RECORD = "/api/task_record?device=%s&offset=%s&limit=%s&is_stop=1";
private static final String UPLOAD_FLIGHT_STATUS = "/api/task/%s/flight";
private static final String TASK_RECORD_2 = "/api/task_record/%s";
List<String> zhiMouRequestPara = Arrays.asList(SysParamsKey.ZHIMOU_AI_URL, SysParamsKey.ZHIMOU_AI_APP_KEY, SysParamsKey.ZHIMOU_AI_APP_SECRET);
private static final Map<String, String> ERROR_CODE_MAP = new HashMap<>();
static {
ERROR_CODE_MAP.put("10000", "无效的平台授权或授权已过期");
ERROR_CODE_MAP.put("10001", "错误的appkey或appsecret");
ERROR_CODE_MAP.put("10002", "无效的接口token");
ERROR_CODE_MAP.put("20000", "无效的zapk安装包");
ERROR_CODE_MAP.put("20001", "错误的应用包格式");
ERROR_CODE_MAP.put("20002", "无效的应用包授权");
ERROR_CODE_MAP.put("20003", "应用包模型安装错误");
ERROR_CODE_MAP.put("20004", "应用已过期");
ERROR_CODE_MAP.put("20005", "无效的应用ID");
ERROR_CODE_MAP.put("20006", "应用配置存储失败");
ERROR_CODE_MAP.put("20007", "应用无可用规则");
ERROR_CODE_MAP.put("30000", "文件写入失败");
ERROR_CODE_MAP.put("30001", "文件删除失败");
ERROR_CODE_MAP.put("40000", "无效的请求体");
ERROR_CODE_MAP.put("50000", "任务输入流或输出流打开失败");
ERROR_CODE_MAP.put("50001", "无效的任务ID");
ERROR_CODE_MAP.put("50002", "无效的任务输出流ID");
ERROR_CODE_MAP.put("50003", "无效的任务状态");
ERROR_CODE_MAP.put("50004", "任务输出流打开失败");
ERROR_CODE_MAP.put("50005", "无效的任务图片数据");
ERROR_CODE_MAP.put("50006", "无效的任务图片ID");
ERROR_CODE_MAP.put("50007", "任务图片上报频次过高");
ERROR_CODE_MAP.put("50008", "任务可用并发数不足");
ERROR_CODE_MAP.put("50009", "开启任务模型加载失败");
}
@Override
public Result<Object> resultHandle(JSONObject result) {
if (result == null) {
return new Result<>().error(ErrorCode.ZHIMOU_REQUEST_ERROR);
}
if (result.containsKey("code")) {
int code = result.getIntValue("code");
if (code == 0) {
return new Result<>().ok(result.get("data"));
} else {
String msg = ERROR_CODE_MAP.get(code + "");
if (StrUtil.isNotBlank(msg)) {
return new Result<>().error(msg);
}
}
}
return new Result<>().error(ErrorCode.ZHIMOU_REQUEST_ERROR);
}
@Override
public ZhiMouInfo getToken() {
Object tokenObj = CacheUtils.get(BusinessConstant.ZHIMOU_TOKEN);
if (tokenObj instanceof ZhiMouInfo) {
return (ZhiMouInfo) tokenObj;
}
List<SysParamsEntity> paramList = paramsDao.selectList(new QueryWrapper<SysParamsEntity>()
.in("param_code", zhiMouRequestPara));
if (CollUtil.isEmpty(paramList) || paramList.size() != zhiMouRequestPara.size()) {
throw new RenException(ErrorCode.ZHIMOU_NO_CONFIG);
}
String url = null, appKey = null, appSecret = null;
for (SysParamsEntity entity : paramList) {
String paramCode = entity.getParamCode();
if (SysParamsKey.ZHIMOU_AI_URL.equals(paramCode)) {
url = entity.getParamValue();
} else if (SysParamsKey.ZHIMOU_AI_APP_KEY.equals(paramCode)) {
appKey = entity.getParamValue();
} else if (SysParamsKey.ZHIMOU_AI_APP_SECRET.equals(paramCode)) {
appSecret = entity.getParamValue();
}
}
if (StrUtil.hasBlank(url, appKey, appSecret)) {
throw new RenException(ErrorCode.ZHIMOU_NO_CONFIG);
}
JSONObject request = new JSONObject();
request.put(APP_KEY, appKey);
request.put(APP_SECRET, appSecret);
String resultStr = HttpUtil.post(url + GET_TOKEN, request.toJSONString());
log.debug("invoke zhiMou getToken url:{}, request: {},result: {}", url + GET_TOKEN, request, resultStr);
if (StrUtil.isEmpty(resultStr)) {
throw new RenException(ErrorCode.ZHIMOU_REQUEST_ERROR);
}
JSONObject result = JSONObject.parse(resultStr);
if (result.getInteger("code") == 0 && result.getString("msg").equals("ok")) {
ZhiMouInfo info = new ZhiMouInfo();
info.setUrl(url);
info.setToken(result.getJSONObject("data").getString("token"));
info.setExpire(result.getJSONObject("data").getLong("expires_at"));
//官方说过期2小时这里设置1小时缓存
CacheUtils.set(BusinessConstant.ZHIMOU_TOKEN, info);
return info;
}
throw new RenException(ErrorCode.ZHIMOU_REQUEST_ERROR);
}
@Override
public JSONObject getApp() {
ZhiMouInfo zhiMouInfo = getToken();
if (StrUtil.isBlank(zhiMouInfo.getToken())) {
throw new RenException(ErrorCode.ZHIMOU_REQUEST_ERROR);
}
String result = HttpUtil.createGet(zhiMouInfo.getUrl() + GET_APP)
.header(AUTHORIZATION, BEARER + zhiMouInfo.getToken()).execute().body();
log.debug("invoke zhiMou getApp result: {}", result);
if (StrUtil.isNotBlank(result)) {
// List<UavAiWarningDTO> aiList = aiWarningService.list(new HashMap<>());
JSONObject data = JSONObject.parse(result);
/*JSONArray apps = data.getJSONObject("data").getJSONArray("apps");
for (Object app : apps) {
JSONObject appObj = (JSONObject) app;
String appId = appObj.getString("app_id");
JSONArray rules = appObj.getJSONArray("rules");
for (Object rule : rules) {
JSONObject ruleObj = (JSONObject) rule;
String ruleId = ruleObj.getString("rule_id");
ruleObj.put("ai_id", null);
ruleObj.put("ai_status", null);
ruleObj.put("ai_warning_level", null);
for (UavAiWarningDTO dto : aiList) {
if (dto.getAppId().equals(appId) && dto.getRuleId().equals(ruleId)) {
ruleObj.put("ai_id", dto.getId());
ruleObj.put("ai_status", dto.getStatus());
ruleObj.put("ai_warning_level", dto.getWarningLevel());
}
}
}
}*/
return data;
}
return null;
}
}

View File

@ -69,4 +69,6 @@ public interface ErrorCode {
int DICT_EXIST = 20028;
int FILE_FORMAT_ERROR = 20029;
int DELETE_FAIL = 20030;
int ZHIMOU_NO_CONFIG = 20031;
int ZHIMOU_REQUEST_ERROR = 20032;
}

View File

@ -58,3 +58,5 @@
20028={0}\u5B57\u5178\u5DF2\u7ECF\u5B58\u5728
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