| | |
| | | package com.ruoyi.envaccess.controller; |
| | | |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import com.ruoyi.common.handler.CustomTenantLineHandler; |
| | | import com.ruoyi.framework.aspectj.lang.annotation.Anonymous; |
| | | import com.ruoyi.framework.aspectj.lang.annotation.Log; |
| | | import com.ruoyi.framework.aspectj.lang.enums.BusinessType; |
| | | import com.ruoyi.framework.web.domain.AjaxResult; |
| | | import com.ruoyi.envaccess.pojo.EnvInspection; |
| | | import com.ruoyi.envaccess.service.IEnvInspectionService; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import javax.annotation.Resource; |
| | | import javax.servlet.http.HttpServletResponse; |
| | | import java.util.ArrayList; |
| | | import java.util.Date; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 环保门禁--智能巡检台账(含路径规划/AI视频分析/报告/无人机机器人,AI与无人机字段预留) |
| | |
| | | public void export(HttpServletResponse response, EnvInspection req) { |
| | | envInspectionService.export(response, req); |
| | | } |
| | | |
| | | @Value("${envaccess.callback.secret:hbmjxt-callback-2026}") |
| | | private String callbackSecret; |
| | | |
| | | private static final ObjectMapper CALLBACK_MAPPER = new ObjectMapper(); |
| | | |
| | | /** |
| | | * 无人机 / 机器人 / AI 摄像头 巡检数据回传(对外开放接口,供第三方设备调用) |
| | | * |
| | | * 请求示例(POST JSON): |
| | | * { |
| | | * "secret": "hbmjxt-callback-2026", |
| | | * "tenantId": 222, |
| | | * "inspectionId": 3, // 可选,不传则按 area 匹配最近一条未取消任务,再没有就新建 |
| | | * "area": "东门区域", |
| | | * "deviceType": "UAV", // UAV 无人机 / ROBOT 机器人 / CAMERA 摄像头 |
| | | * "deviceCode": "UAV-M350-001", |
| | | * "collectedAt": "2026-10-24 09:12:35", |
| | | * "aiSummary": "共识别 5 类目标,发现 1 处未佩戴安全帽", |
| | | * "aiObjects": [{"object":"人员未佩戴安全帽","conclusion":"未按要求佩戴","confidence":0.93,"frameTime":"2026-10-24 09:12:35"}], |
| | | * "longitude": 120.382145, "latitude": 31.298512, "altitude": 45.6, "battery": 78, |
| | | * "photos": ["/profile/upload/uav_east_01.jpg"], |
| | | * "track": [{"t":"09:12:10","lng":120.3821,"lat":31.2985}] |
| | | * } |
| | | */ |
| | | @Anonymous |
| | | @PostMapping("/callback") |
| | | public AjaxResult callback(@RequestBody Map<String, Object> payload) { |
| | | if (payload == null || payload.isEmpty()) { |
| | | return AjaxResult.error("回传数据不能为空"); |
| | | } |
| | | if (!callbackSecret.equals(String.valueOf(payload.get("secret")))) { |
| | | return AjaxResult.error("secret 校验失败,请在请求体中携带正确的 secret"); |
| | | } |
| | | Long tenantId = toLong(payload.get("tenantId")); |
| | | if (tenantId == null) { |
| | | return AjaxResult.error("tenantId 不能为空"); |
| | | } |
| | | String area = payload.get("area") == null ? "未指定区域" : String.valueOf(payload.get("area")); |
| | | String deviceType = payload.get("deviceType") == null ? "UAV" : String.valueOf(payload.get("deviceType")); |
| | | String deviceCode = payload.get("deviceCode") == null ? deviceType + "-UNKNOWN" : String.valueOf(payload.get("deviceCode")); |
| | | |
| | | Map<String, Object> ai = new LinkedHashMap<>(); |
| | | ai.put("summary", payload.get("aiSummary") == null ? "本次回传未附带 AI 识别结论" : payload.get("aiSummary")); |
| | | ai.put("objects", payload.get("aiObjects") == null ? new ArrayList<>() : payload.get("aiObjects")); |
| | | |
| | | Map<String, Object> uav = new LinkedHashMap<>(); |
| | | uav.put("deviceType", deviceType); |
| | | uav.put("deviceCode", deviceCode); |
| | | uav.put("area", area); |
| | | uav.put("collectedAt", payload.get("collectedAt") == null ? "" : payload.get("collectedAt")); |
| | | uav.put("longitude", payload.get("longitude")); |
| | | uav.put("latitude", payload.get("latitude")); |
| | | uav.put("altitude", payload.get("altitude")); |
| | | uav.put("battery", payload.get("battery")); |
| | | uav.put("photos", payload.get("photos") == null ? new ArrayList<>() : payload.get("photos")); |
| | | uav.put("track", payload.get("track") == null ? new ArrayList<>() : payload.get("track")); |
| | | uav.put("receivedAt", new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); |
| | | |
| | | String aiJson; |
| | | String uavJson; |
| | | try { |
| | | aiJson = CALLBACK_MAPPER.writeValueAsString(ai); |
| | | uavJson = CALLBACK_MAPPER.writeValueAsString(uav); |
| | | } catch (Exception e) { |
| | | return AjaxResult.error("回传数据序列化失败:" + e.getMessage()); |
| | | } |
| | | |
| | | EnvInspection target = null; |
| | | Long inspectionId = toLong(payload.get("inspectionId")); |
| | | if (inspectionId != null) { |
| | | target = envInspectionService.getById(inspectionId); |
| | | } |
| | | if (target == null) { |
| | | com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<EnvInspection> w = |
| | | new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<>(); |
| | | w.eq(EnvInspection::getArea, area).ne(EnvInspection::getStatus, 3) |
| | | .orderByDesc(EnvInspection::getId).last("limit 1"); |
| | | target = envInspectionService.getOne(w, false); |
| | | } |
| | | |
| | | boolean created = false; |
| | | if (target == null) { |
| | | target = new EnvInspection(); |
| | | target.setInspectionName(area + " 智能巡检(设备回传)"); |
| | | target.setInspectionType(2); |
| | | target.setArea(area); |
| | | target.setStatus(2); |
| | | target.setPlanStartTime(new Date()); |
| | | target.setPlanEndTime(new Date()); |
| | | target.setActualStartTime(new Date()); |
| | | target.setActualEndTime(new Date()); |
| | | target.setInspector(deviceCode); |
| | | target.setRoutePlan("由 " + deviceType + " 自动规划并执行"); |
| | | target.setInspectionResult("设备自动巡检已完成,识别结果详见回传数据。"); |
| | | target.setTenantId(tenantId); |
| | | created = true; |
| | | } |
| | | target.setAiAnalysis(aiJson); |
| | | target.setUavData(uavJson); |
| | | if (target.getStatus() != null && target.getStatus() == 0) { |
| | | target.setStatus(2); |
| | | } |
| | | if (target.getTenantId() == null) { |
| | | target.setTenantId(tenantId); |
| | | } |
| | | |
| | | // 设备回调没有登录上下文,临时跳过租户插件,tenant_id 已在上面显式写入 |
| | | CustomTenantLineHandler.TEMP_IGNORE.set(Boolean.TRUE); |
| | | try { |
| | | if (created) { |
| | | envInspectionService.save(target); |
| | | } else { |
| | | envInspectionService.updateById(target); |
| | | } |
| | | } catch (Exception e) { |
| | | return AjaxResult.error("回传数据落库失败:" + e.getMessage()); |
| | | } finally { |
| | | CustomTenantLineHandler.TEMP_IGNORE.remove(); |
| | | } |
| | | |
| | | Map<String, Object> result = new LinkedHashMap<>(); |
| | | result.put("inspectionId", target.getId()); |
| | | result.put("created", created); |
| | | result.put("deviceCode", deviceCode); |
| | | result.put("deviceType", deviceType); |
| | | result.put("area", area); |
| | | return AjaxResult.success("巡检数据回传成功", result); |
| | | } |
| | | |
| | | private Long toLong(Object v) { |
| | | if (v == null) { |
| | | return null; |
| | | } |
| | | try { |
| | | return Long.valueOf(String.valueOf(v).trim()); |
| | | } catch (NumberFormatException e) { |
| | | return null; |
| | | } |
| | | } |
| | | } |