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 db05289..e4e1006 100644 --- a/admin/src/main/java/com/multictrl/common/constant/BusinessConstant.java +++ b/admin/src/main/java/com/multictrl/common/constant/BusinessConstant.java @@ -8,9 +8,10 @@ package com.multictrl.common.constant; */ public interface BusinessConstant { - String ZHIMOU_AI_CALLBACK_TOPIC = "thing/product/zhimou_ai_callback"; + String ZHIMOU_AI_CALLBACK_TOPIC = "thing/product/%s/zhimou_ai_callback"; String WEB_EVENT_TOPIC = "thing/product/%s/web_event"; String NOFLY_ZONE_METHOD = "nofly_zone"; + String ZHIMOU_AI_CALLBACK = "zhimou_ai_callback"; //********************************* minio *********************************// String ROUTE_IMG_BUCKET = "route-images";//航线图片桶 @@ -91,7 +92,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_"; + String DOCK_ZHIMOU_CONFIG = "dock_zhiMou_config_"; //********************************* other *********************************// String HTTP_PROTOCOL = "http://"; 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 451478f..0229348 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 @@ -1,18 +1,28 @@ package com.multictrl.modules.business.controller; +import com.alibaba.fastjson2.JSONArray; 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.DownloadReport; +import com.multictrl.modules.business.dto.zhimou.Osd; 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 jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + /** * 智眸AI接口封装 * @@ -29,7 +39,7 @@ public class ZhiMouController { @Operation(summary = "获取应用列表") @GetMapping("/getApp") - @RequiresPermissions("bus:zhimou:getApp") + @RequiresPermissions("bus:zhimou:get") public Result getApp() { JSONObject data = zhiMouService.getApp(); return zhiMouService.resultHandle(data); @@ -38,7 +48,7 @@ public class ZhiMouController { @Operation(summary = "开始AI检测任务") @LogOperation("开始AI检测任务") @PostMapping("/startByWeb") - @RequiresPermissions("bus:zhimou:startByWeb") + @RequiresPermissions("bus:zhimou:start") public Result startByWeb(@RequestBody ZhiMouAiStart zhiMouAiStart) { ValidatorUtils.validateEntity(zhiMouAiStart); JSONObject data = zhiMouService.startByWeb(zhiMouAiStart); @@ -48,9 +58,55 @@ public class ZhiMouController { @Operation(summary = "停止AI检测任务") @LogOperation("停止AI检测任务") @PostMapping("/stopByWeb") - @RequiresPermissions("bus:zhimou:stopByWeb") + @RequiresPermissions("bus:zhimou:stop") public Result stopByWeb(@RequestParam String dockSn) { JSONObject data = zhiMouService.stopByWeb(dockSn); return zhiMouService.resultHandle(data); } + + @Operation(summary = "获取AI检测任务") + @GetMapping("/getRunningTaskByWeb") + public Result getRunningTaskByWeb(@RequestParam String dockSn) { + JSONObject data = zhiMouService.getRunningTaskByWeb(dockSn); + return new Result<>().ok(data); + } + + @Operation(summary = "配置AI检测任务") + @LogOperation("配置AI检测任务") + @PostMapping("/configurationByWeb") + @RequiresPermissions("bus:zhimou:config") + public Result configurationByWeb(@RequestBody Osd osd) { + JSONObject data = zhiMouService.configurationByWeb(osd); + return zhiMouService.resultHandle(data); + } + + @Operation(summary = "获取实时检测结果") + @GetMapping("/currentTaskMessageRecord") + public Result currentTaskMessageRecord(@RequestParam String dockSn) { + JSONObject data = zhiMouService.currentTaskMessageRecord(dockSn); + return zhiMouService.resultHandle(data); + } + + @Operation(summary = "获取飞行任务检测结果") + @GetMapping("/getTaskMessageRecord") + @RequiresPermissions("bus:zhiMou:image") + public Result getTaskMessageRecordList(@RequestParam String taskId) { + JSONArray data = zhiMouService.getTaskMessageRecordList(taskId); + return new Result<>().ok(data); + } + + @Operation(summary = "下载AI检测报告") + @PostMapping("/downloadReport") + @RequiresPermissions("ai:zhiMou:downloadReport") + public ResponseEntity getReportByBusinessId(@RequestBody DownloadReport downloadReport) { + ValidatorUtils.validateEntity(downloadReport); + byte[] reportBytes = zhiMouService.downloadReport(downloadReport); + + String fileName = "AI报告_" + downloadReport.getTaskId() + ".zip"; + String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8); + return ResponseEntity.ok() + .contentType(MediaType.APPLICATION_OCTET_STREAM) + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + encodedFileName) + .body(reportBytes); + } } diff --git a/admin/src/main/java/com/multictrl/modules/business/dao/ZhimouCallbackDao.java b/admin/src/main/java/com/multictrl/modules/business/dao/ZhimouCallbackDao.java new file mode 100644 index 0000000..ac0fe2b --- /dev/null +++ b/admin/src/main/java/com/multictrl/modules/business/dao/ZhimouCallbackDao.java @@ -0,0 +1,16 @@ +package com.multictrl.modules.business.dao; + +import com.multictrl.common.dao.BaseDao; +import com.multictrl.modules.business.entity.ZhimouCallbackEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * 智眸事件回调 + * + * @author Sdy + * @since 1.0.0 2026-06-11 + */ +@Mapper +public interface ZhimouCallbackDao extends BaseDao { + +} \ No newline at end of file diff --git a/admin/src/main/java/com/multictrl/modules/business/dto/zhimou/DownloadReport.java b/admin/src/main/java/com/multictrl/modules/business/dto/zhimou/DownloadReport.java new file mode 100644 index 0000000..b5c13af --- /dev/null +++ b/admin/src/main/java/com/multictrl/modules/business/dto/zhimou/DownloadReport.java @@ -0,0 +1,34 @@ +package com.multictrl.modules.business.dto.zhimou; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +/** + * 下载报告参数 + * + * @author Sdy + * @since 1.0.0 2026/6/11 + */ +@Schema(name = "AI报告参数") +@Data +public class DownloadReport { + @Schema(description = "标题") + private String title; + + @Schema(description = "子标题") + private String subtitle; + + @Schema(description = "报告单位") + private String reporter; + + @Schema(description = "支撑单位") + private String supporter; + + @Schema(description = "备注") + private String remark; + + @NotBlank(message = "架次编号不能为空") + @Schema(description = "架次编号") + private String taskId; +} diff --git a/admin/src/main/java/com/multictrl/modules/business/dto/zhimou/Osd.java b/admin/src/main/java/com/multictrl/modules/business/dto/zhimou/Osd.java index cef3457..dd7a151 100644 --- a/admin/src/main/java/com/multictrl/modules/business/dto/zhimou/Osd.java +++ b/admin/src/main/java/com/multictrl/modules/business/dto/zhimou/Osd.java @@ -68,7 +68,7 @@ public class Osd { */ private List> polygon; - private String deviceId; + private String dockSn; private String taskId; } diff --git a/admin/src/main/java/com/multictrl/modules/business/entity/ZhimouCallbackEntity.java b/admin/src/main/java/com/multictrl/modules/business/entity/ZhimouCallbackEntity.java new file mode 100644 index 0000000..f3c8942 --- /dev/null +++ b/admin/src/main/java/com/multictrl/modules/business/entity/ZhimouCallbackEntity.java @@ -0,0 +1,50 @@ +package com.multictrl.modules.business.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.util.Date; + +/** + * 智眸事件回调 + * + * @author Sdy + * @since 1.0.0 2026-06-11 + */ +@Data +@TableName("bus_zhimou_callback") +public class ZhimouCallbackEntity { + + @TableId + private Long id; + /** + * $column.comments + */ + private String appId; + /** + * 飞控任务id + */ + private String businessId; + /** + * 机库SN + */ + private String device; + /** + * 事件 + */ + private String event; + /** + * 智眸任务id + */ + private String taskId; + /** + * 上报时间 + */ + private Long timestamp; + + @TableField(fill = FieldFill.INSERT) + private Date createDate; +} \ No newline at end of file diff --git a/admin/src/main/java/com/multictrl/modules/business/handler/TopicDistributor.java b/admin/src/main/java/com/multictrl/modules/business/handler/TopicDistributor.java index 1db28b6..82119f2 100644 --- a/admin/src/main/java/com/multictrl/modules/business/handler/TopicDistributor.java +++ b/admin/src/main/java/com/multictrl/modules/business/handler/TopicDistributor.java @@ -30,6 +30,7 @@ public class TopicDistributor { private final StateHandler stateHandler; private final ServicesReplyHandler servicesReplyHandler; private final EventsHandler eventsHandler; + private final ZhiMouCallbackHandler zhiMouCallbackHandler; private final Q20OsdTopicHandler q20OsdTopicHandler; private final Q20EventsHandler q20EventsHandler; private final Q20StateTopicHandler q20StateTopicHandler; @@ -48,6 +49,7 @@ public class TopicDistributor { handlerMap.put(BusinessConstant.STATE, stateHandler); handlerMap.put(BusinessConstant.EVENTS, eventsHandler); handlerMap.put(BusinessConstant.SERVICES_REPLY, servicesReplyHandler); + handlerMap.put(BusinessConstant.ZHIMOU_AI_CALLBACK, zhiMouCallbackHandler); q20HandlerMap.put(BusinessConstant.OSD, q20OsdTopicHandler); q20HandlerMap.put(BusinessConstant.EVENTS, q20EventsHandler); diff --git a/admin/src/main/java/com/multictrl/modules/business/handler/ZhiMouCallbackHandler.java b/admin/src/main/java/com/multictrl/modules/business/handler/ZhiMouCallbackHandler.java new file mode 100644 index 0000000..8f42db4 --- /dev/null +++ b/admin/src/main/java/com/multictrl/modules/business/handler/ZhiMouCallbackHandler.java @@ -0,0 +1,41 @@ +package com.multictrl.modules.business.handler; + +import cn.hutool.json.JSONObject; +import com.multictrl.common.utils.JsonUtils; +import com.multictrl.modules.business.dao.ZhimouCallbackDao; +import com.multictrl.modules.business.entity.ZhimouCallbackEntity; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 智眸回调处理 + * + * @author Sdy + * @since 1.0.0 2026/6/11 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class ZhiMouCallbackHandler implements MessageHandler{ + private final ZhimouCallbackDao zhimouCallbackDao; + + @Override + public void handleMessage(String topic, String payload, String gateway) { + log.debug("zhiMouCallback --> topic: {}, payload: {}", topic, payload); + JSONObject data = JsonUtils.parseObject(payload, JSONObject.class); + if(data!=null){ + String event = data.getStr("event"); + if("start".equals(event)||"stop".equals(event)){ + ZhimouCallbackEntity entity = new ZhimouCallbackEntity(); + entity.setAppId(data.getStr("appId")); + entity.setBusinessId(data.getStr("business_id")); + entity.setDevice(data.getStr("device")); + entity.setEvent(event); + entity.setTaskId(data.getStr("task_id")); + entity.setTimestamp(data.getLong("timestamp")); + zhimouCallbackDao.insert(entity); + } + } + } +} 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 cc4daf5..51fa508 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 @@ -1,9 +1,13 @@ package com.multictrl.modules.business.service; +import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONObject; import com.multictrl.common.utils.Result; +import com.multictrl.modules.business.dto.zhimou.DownloadReport; +import com.multictrl.modules.business.dto.zhimou.Osd; import com.multictrl.modules.business.dto.zhimou.ZhiMouAiStart; import com.multictrl.modules.business.dto.zhimou.ZhiMouInfo; +import jakarta.servlet.http.HttpServletResponse; /** * 智眸AI接口封装 @@ -27,4 +31,22 @@ public interface ZhiMouService { //停止AI检测任务 JSONObject stopByWeb(String dockSn); + + //获取AI检测任务 + JSONObject getRunningTaskByWeb(String dockSn); + + //配置ai检测任务 + JSONObject configurationByWeb(Osd osd); + + //获取实时检测结果 + JSONObject currentTaskMessageRecord(String dockSn); + + //获取信息记录 + JSONObject getMessageRecord(String taskId); + + //获取飞行任务检测结果 + JSONArray getTaskMessageRecordList(String taskId); + + //下载AI检测报告 + byte[] downloadReport(DownloadReport downloadReport); } 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 10ba3a1..3b63b85 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 @@ -5,10 +5,12 @@ 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.JSON; 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.config.SysConfig; import com.multictrl.common.constant.BusinessConstant; import com.multictrl.common.constant.SysParamsKey; import com.multictrl.common.exception.ErrorCode; @@ -16,21 +18,25 @@ import com.multictrl.common.exception.RenException; import com.multictrl.common.utils.CacheUtils; import com.multictrl.common.utils.Result; import com.multictrl.common.utils.Utils; +import com.multictrl.modules.business.dao.ZhimouCallbackDao; import com.multictrl.modules.business.dto.zhimou.*; +import com.multictrl.modules.business.entity.ZhimouCallbackEntity; 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; +import jakarta.servlet.http.HttpServletResponse; 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; +import java.io.ByteArrayOutputStream; +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; /** * 智眸AI接口封装 @@ -45,6 +51,8 @@ public class ZhiMouServiceImpl implements ZhiMouService { private final SysParamsDao paramsDao; private final SrsService srsService; private final MqttConfig mqttConfig; + private final SysConfig sysConfig; + private final ZhimouCallbackDao zhimouCallbackDao; private static final String APP_KEY = "appkey"; private static final String APP_SECRET = "appsecret"; private static final String AUTHORIZATION = "Authorization"; @@ -237,7 +245,7 @@ public class ZhiMouServiceImpl implements ZhiMouService { mqtt.setClientID("zhi_mou_ai_" + IdUtil.getSnowflakeNextIdStr()); mqtt.setUsername(client1.getUsername()); mqtt.setPassword(client1.getPassword()); - mqtt.setTopic(BusinessConstant.ZHIMOU_AI_CALLBACK_TOPIC); + mqtt.setTopic(BusinessConstant.ZHIMOU_AI_CALLBACK_TOPIC.formatted(dockSn)); mqtt.setQos(0L); report.setMqtt(mqtt); zhiMouTask.setReport(report); @@ -251,22 +259,18 @@ public class ZhiMouServiceImpl implements ZhiMouService { .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 JSONObject.parse(result); } return null; } @Override public JSONObject stopByWeb(String dockSn) { - String taskId = (String) CacheUtils.get(BusinessConstant.DOCK_ZHIMOU_TASK_ID + dockSn); - if (StrUtil.isBlank(taskId)) { + JSONObject running = getRunningTaskByWeb(dockSn); + if (running == null) { throw new RenException(ErrorCode.ZHIMOU_TASK_NOT_EXIST); } + String taskId = running.getString("task_id"); ZhiMouInfo zhiMouInfo = getToken(); String result = HttpUtil.createRequest(Method.DELETE, zhiMouInfo.getUrl() + TASK + "/" + taskId) .header(AUTHORIZATION, BEARER + zhiMouInfo.getToken()) @@ -279,6 +283,190 @@ public class ZhiMouServiceImpl implements ZhiMouService { return null; } + @Override + public JSONObject getRunningTaskByWeb(String dockSn) { + ZhiMouInfo zhiMouInfo = getToken(); + String result = HttpUtil.createGet(zhiMouInfo.getUrl() + TASK) + .header(AUTHORIZATION, BEARER + zhiMouInfo.getToken()).execute().body(); + log.debug("invoke zhiMou getRunningTask result: {}", result); + if (StrUtil.isNotBlank(result)) { + JSONObject runningTask = JSONObject.parse(result); + JSONObject jsonObject = runningTask.getJSONObject("data"); + if (jsonObject != null) { + JSONArray jsonArray = jsonObject.getJSONArray("tasks"); + if (jsonArray != null && !jsonArray.isEmpty()) { + for (int i = 0; i < jsonArray.size(); i++) { + JSONObject j2 = jsonArray.getJSONObject(i); + if (j2.getString("device").equals(dockSn)) { + String protocol = BusinessConstant.HTTP_PROTOCOL; + if (sysConfig.getIsSsl()) { + protocol = BusinessConstant.HTTPS_PROTOCOL; + } + j2.remove("report"); + j2.remove("input"); + j2.remove("output"); + j2.remove("dji"); + j2.put("appId", j2.getString("app_id")); + j2.remove("app_id"); + j2.put("businessId", j2.getString("business_id")); + j2.remove("business_id"); + String webrtcUrl = protocol + sysConfig.getIp() + ":" + sysConfig.getPort() + "/rtc/v1/whep/?app=" + dockSn + "&stream=uav_ai.flv"; + j2.put("webrtcUrl", webrtcUrl); + j2.put("osd", CacheUtils.get(BusinessConstant.DOCK_ZHIMOU_CONFIG + dockSn)); + return j2; + } + } + } + } + } + return null; + } + + @Override + public JSONObject configurationByWeb(Osd osd) { + String dockSn = osd.getDockSn(); + JSONObject running = getRunningTaskByWeb(dockSn); + if (running == null) { + throw new RenException(ErrorCode.ZHIMOU_TASK_NOT_EXIST); + } + osd.setTaskId(running.getString("task_id")); + JSONObject osdObject = Utils.beanToSnakeJsonFastJson(osd); + ZhiMouInfo zhiMouInfo = getToken(); + String result = HttpUtil.createRequest(Method.PUT, + zhiMouInfo.getUrl() + TASK + "/" + osd.getTaskId()) + .body(osdObject.toJSONString()) + .header(AUTHORIZATION, BEARER + zhiMouInfo.getToken()) + .execute() + .body(); + log.debug("invoke zhiMou configuration request: {},result: {}", osdObject, result); + if (StrUtil.isNotBlank(result)) { + JSONObject parse = JSONObject.parse(result); + if (parse != null && parse.containsKey("ok")) { + CacheUtils.set(BusinessConstant.DOCK_ZHIMOU_CONFIG + dockSn, osd); + } + return parse; + } + return null; + } + + @Override + public JSONObject currentTaskMessageRecord(String dockSn) { + JSONObject running = getRunningTaskByWeb(dockSn); + if (running == null) { + throw new RenException(ErrorCode.ZHIMOU_TASK_NOT_EXIST); + } + String taskId = running.getString("task_id"); + + return getMessageRecord(taskId); + } + + @Override + public JSONObject getMessageRecord(String taskId) { + ZhiMouInfo zhiMouInfo = getToken(); + String result = HttpUtil.createGet(zhiMouInfo.getUrl() + MESSAGE_RECORD + + String.format("?task_id=%s&limit=%s", taskId, Integer.MAX_VALUE)) + .header(AUTHORIZATION, BEARER + zhiMouInfo.getToken()) + .header("Referer", zhiMouInfo.getUrl()) + .execute().body(); + log.debug("invoke zhiMou getMessageRecord taskId:{} result: {}", taskId, result); + if (!result.contains("\"code\":0")) { + JSONObject temp = new JSONObject(); + temp.put("code", 0); + temp.put("data", new JSONArray()); + return temp; + } + JSONObject jsonObject = JSONObject.parseObject(result); + JSONArray data = jsonObject.getJSONArray("data"); + if (data == null || data.isEmpty()) { + JSONObject temp = new JSONObject(); + temp.put("code", 0); + temp.put("data", new JSONArray()); + return temp; + } + for (int i = 0; i < data.size(); i++) { + JSONObject t = data.getJSONObject(i); + JSONArray t2 = t.getJSONArray("objects"); + int count = 0; + if (t2 != null) { + count = t2.size(); + } + //筛选出带车牌的事件 + JSONArray carIds = new JSONArray(); + if (t2 != null) { + for (int j = 0; j < t2.size(); j++) { + JSONObject t3 = t2.getJSONObject(j); + if (t3.containsKey("attr_name") && "车牌".equals(t3.getString("attr_name"))) { + carIds.add(t3); + } + } + } + t.remove("objects"); + if (!carIds.isEmpty()) { + t.put("objects", carIds); + } + t.put("count", count); + t.put("thumbnail", zhiMouInfo.getUrl() + "/thumbnail" + t.getString("image")); + t.put("image", zhiMouInfo.getUrl() + t.getString("image")); + } + return jsonObject; + } + + @Override + public JSONArray getTaskMessageRecordList(String taskId) { + List entityList = zhimouCallbackDao.selectList(new QueryWrapper() + .eq("business_id", taskId)); + if (CollUtil.isEmpty(entityList)) { + throw new RenException(ErrorCode.ZHIMOU_TASK_NOT_EXIST); + } + Set taskIds = entityList.stream().map(ZhimouCallbackEntity::getTaskId).collect(Collectors.toSet()); + JSONArray jsonArray = new JSONArray(); + for (String zhimouTaskId : taskIds) { + JSONObject result = getMessageRecord(zhimouTaskId); + jsonArray.addAll(result.getJSONArray("data")); + } + jsonArray = jsonArray.stream().map(obj -> (JSONObject) obj) + .sorted(Comparator.comparingLong( + o -> o.getLong("id"))) + .collect(JSONArray::new, JSONArray::add, JSONArray::addAll); + return jsonArray; + } + + @Override + public byte[] downloadReport(DownloadReport downloadReport) { + List entityList = zhimouCallbackDao.selectList(new QueryWrapper() + .eq("business_id", downloadReport.getTaskId())); + if (CollUtil.isEmpty(entityList)) { + throw new RenException(ErrorCode.ZHIMOU_TASK_NOT_EXIST); + } + Set taskIds = entityList.stream().map(ZhimouCallbackEntity::getTaskId).collect(Collectors.toSet()); + ZhiMouInfo zhiMouInfo = getToken(); + try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ZipOutputStream zipOut = new ZipOutputStream(baos)) { + + for (String taskId : taskIds) { + JSONObject para = JSON.parseObject(JSON.toJSONString(downloadReport)); + para.remove("taskId"); + para.put("rule_ids", new JSONArray()); + byte[] fileBytes = HttpUtil.createPost(zhiMouInfo.getUrl() + String.format(REPORT, taskId)) + .header(AUTHORIZATION, BEARER + zhiMouInfo.getToken()) + .body(para.toJSONString()) + .execute() + .bodyBytes(); + + String fileName = para.getString("title") + "_" + taskId + ".docx"; + ZipEntry zipEntry = new ZipEntry(fileName); + zipOut.putNextEntry(zipEntry); + zipOut.write(fileBytes); + zipOut.closeEntry(); + } + + return baos.toByteArray(); + } catch (Exception e) { + log.error("下载 AI报告ZIP 失败", e); + throw new RuntimeException("下载AI报告失败", e); + } + } + private String replaceRtmpIpPort(String originalUrl, String newIp, String newPort) { // 定义RTMP URL的正则表达式模式 Pattern pattern = Pattern.compile("^rtmp://([^/:]+):([^/]+)(/.*)$");