| | |
| | | import com.alibaba.fastjson2.JSON; |
| | | import com.ard.common.core.constant.SecurityConstants; |
| | | import com.ard.common.core.domain.R; |
| | | import com.ard.common.redis.service.RedisService; |
| | | import com.ard.system.api.RemoteDictService; |
| | | import com.ard.system.api.domain.SysDictData; |
| | | import com.ard.work.api.RemoteCameraService; |
| | |
| | | import org.springframework.ai.tool.annotation.Tool; |
| | | import org.springframework.ai.tool.annotation.ToolParam; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | | |
| | | @Slf4j |
| | |
| | | private RemoteCameraService remoteCameraService; |
| | | @Resource |
| | | private RemoteDictService remoteDictService; |
| | | @Resource |
| | | private RedisService redisService; // 若依的RedisCache |
| | | // 字典缓存Key前缀(若依默认) |
| | | private static final String DICT_KEY_PREFIX = "sys_dict:"; |
| | | |
| | | /** |
| | | * 获取厂商名称(带Redis缓存) |
| | | */ |
| | | private String getFactoryName(String factoryCode) { |
| | | return getDictLabel("factory", factoryCode); |
| | | } |
| | | |
| | | /** |
| | | * 获取相机类型名称(带Redis缓存) |
| | | */ |
| | | private String getCameraTypeName(String typeCode) { |
| | | return getDictLabel("camera_type", typeCode); |
| | | } |
| | | /** |
| | | * 获取字典标签(直接从Redis缓存读取) |
| | | */ |
| | | private String getDictLabel(String dictType, String dictValue) { |
| | | if (dictValue == null || dictValue.isEmpty()) { |
| | | return "未知"; |
| | | } |
| | | |
| | | // 若依框架:从缓存获取字典数据 |
| | | List<SysDictData> dictDatas = redisService.getCacheObject("sys_dict:" + dictType); |
| | | if (dictDatas == null || dictDatas.isEmpty()) { |
| | | // 如果缓存为空,可以从远程服务加载一次 |
| | | R<List<SysDictData>> result = remoteDictService.dictTypeRpc(dictType, SecurityConstants.INNER); |
| | | if (result != null && result.getCode() == R.SUCCESS) { |
| | | dictDatas = result.getData(); |
| | | // 重新放入缓存 |
| | | redisService.setCacheObject("sys_dict:" + dictType, dictDatas); |
| | | } |
| | | } |
| | | |
| | | if (dictDatas != null) { |
| | | return dictDatas.stream() |
| | | .filter(dict -> dictValue.equals(dict.getDictValue())) |
| | | .map(SysDictData::getDictLabel) |
| | | .findFirst() |
| | | .orElse("未知(" + dictValue + ")"); |
| | | } |
| | | |
| | | return "未知(" + dictValue + ")"; |
| | | } |
| | | |
| | | @Tool(description = "获取指定摄像机的详细信息,包括IP、名称、状态等") |
| | | public String getCameraDetail(@ToolParam(description = "摄像机ID,如CAM-001") String cameraId) { |
| | |
| | | } |
| | | |
| | | List<ArdCamera> filteredCameras = cameras; |
| | | // if (keyword != null && !keyword.isEmpty()) { |
| | | // filteredCameras = cameras.stream() |
| | | // .filter(c -> (c.getName() != null && c.getName().contains(keyword)) || |
| | | // (c.getId() != null && c.getId().contains(keyword))) |
| | | // .toList(); |
| | | // } |
| | | |
| | | if (filteredCameras.isEmpty()) { |
| | | return "未找到符合条件的摄像机"; |
| | | } |
| | | R<List<SysDictData>> getFactoryResult = |
| | | remoteDictService.dictTypeRpc("factory", SecurityConstants.INNER); |
| | | if (getFactoryResult == null || getFactoryResult.getCode() != R.SUCCESS) { |
| | | return String.format("获取厂商信息失败:%s", |
| | | getFactoryResult != null ? getFactoryResult.getMsg() : "服务返回空结果"); |
| | | } |
| | | R<List<SysDictData>> getCameraTypeResult = |
| | | remoteDictService.dictTypeRpc("camera_type", SecurityConstants.INNER); |
| | | if (getCameraTypeResult == null || getCameraTypeResult.getCode() != R.SUCCESS) { |
| | | return String.format("获取相机类型失败:%s", |
| | | getCameraTypeResult != null ? getCameraTypeResult.getMsg() : "服务返回空结果"); |
| | | } |
| | | |
| | | StringBuilder resultStr = new StringBuilder(); |
| | | resultStr.append(String.format("找到%d台摄像机:\n", filteredCameras.size())); |
| | | for (ArdCamera camera : filteredCameras) { |
| | | |
| | | String factoryName = getFactoryResult.getData().stream() |
| | | .filter(dict -> camera.getFactory().equals(dict.getDictValue())) |
| | | .findFirst() |
| | | .map(SysDictData::getDictLabel) |
| | | .orElseGet(() -> { |
| | | log.warn("未找到厂商代码对应的字典项: {}", camera.getFactory()); |
| | | return "未知厂商(" + camera.getFactory() + ")"; |
| | | }); |
| | | String cameraTypeName = getCameraTypeResult.getData().stream() |
| | | .filter(dict -> camera.getType().equals(dict.getDictValue())) |
| | | .findFirst() |
| | | .map(SysDictData::getDictLabel) |
| | | .orElseGet(() -> { |
| | | log.warn("未找到相机类型对应的字典项: {}", camera.getType()); |
| | | return "未知厂商(" + camera.getType() + ")"; |
| | | }); |
| | | |
| | | resultStr.append(String.format(" - ID: %s, 名称: %s, IP: %s, 厂商: %s,状态: %s, 类型: %s\n", |
| | | camera.getId(), |
| | | camera.getName(), |
| | | camera.getIp(), |
| | | factoryName, |
| | | getFactoryName(camera.getFactory()), |
| | | Objects.equals(camera.getState(), "1") ? "在线" : "离线", |
| | | cameraTypeName)); |
| | | getCameraTypeName(camera.getType()) |
| | | )); |
| | | } |
| | | return resultStr.toString(); |
| | | } catch (Exception e) { |
| | |
| | | return "系统异常:" + e.getMessage(); |
| | | } |
| | | } |
| | | @Tool(description = """ |
| | | 修改摄像机信息。传入摄像机ID和需要修改的字段,不传的字段保持原值不变。 |
| | | |
| | | @Tool(description = "修改摄像机信息,支持修改名称、IP地址、端口、位置、状态等字段。注意:摄像机ID不能修改") |
| | | 使用示例: |
| | | - "把CAM-001的名字改成大门摄像头" → 传入 {id:"CAM-001", name:"大门摄像头"} |
| | | - "将CAM-002设置为离线" → 传入 {id:"CAM-002", state:"0"} |
| | | |
| | | 注意:id为必填项。 |
| | | """) |
| | | public String updateCameraInfo( |
| | | @ToolParam(description = "摄像机ID,如CAM-001") String cameraId, |
| | | @ToolParam(description = "摄像机名称,如不需要修改可不传") String name, |
| | | @ToolParam(description = "摄像机IP地址,如不需要修改可不传") String ip, |
| | | @ToolParam(description = "摄像机位置/区域,如不需要修改可不传") String location, |
| | | @ToolParam(description = "摄像机状态:1-在线,0-离线,如不需要修改可不传") String state, |
| | | @ToolParam(description = "摄像机类型,如不需要修改可不传") String type, |
| | | @ToolParam(description = "摄像机厂家/品牌,如不需要修改可不传") String factory |
| | | ) { |
| | | log.info("AI调用updateCameraInfo工具,参数: cameraId={}, name={}, ip={}, location={}, state={}, type={}, factory={}", |
| | | cameraId, name, ip, location, state, type, factory); |
| | | @ToolParam(description = "摄像机对象,包含id和需要修改的字段") ArdCamera updateParams) { |
| | | |
| | | // 参数校验 |
| | | if (cameraId == null || cameraId.isEmpty()) { |
| | | log.info("AI调用updateCameraInfo工具,参数: {}", updateParams); |
| | | |
| | | // 必填校验 |
| | | if (updateParams == null || updateParams.getId() == null || updateParams.getId().isEmpty()) { |
| | | return "错误:摄像机ID不能为空"; |
| | | } |
| | | |
| | | String cameraId = updateParams.getId(); |
| | | |
| | | try { |
| | | // 1. 先获取当前摄像机信息 |
| | | R<ArdCamera> getResult = remoteCameraService.getCameraInfoRpc(cameraId, SecurityConstants.INNER); |
| | | |
| | | if (getResult == null || getResult.getCode() != R.SUCCESS) { |
| | | return String.format("获取摄像机信息失败:%s", |
| | | getResult != null ? getResult.getMsg() : "服务返回空结果"); |
| | | // 3. 直接调用 Feign 接口 |
| | | R<Boolean> updateResult = remoteCameraService.updateCamera(updateParams, SecurityConstants.INNER); |
| | | |
| | | if (updateResult == null || updateResult.getCode() != R.SUCCESS) { |
| | | return "修改失败:" + (updateResult != null ? updateResult.getMsg() : "服务返回空"); |
| | | } |
| | | |
| | | ArdCamera camera = getResult.getData(); |
| | | if (camera == null) { |
| | | return "错误:未找到摄像机ID为 " + cameraId + " 的摄像机"; |
| | | } |
| | | |
| | | // 2. 构建更新对象(只更新有值的字段) |
| | | ArdCamera updateCamera = new ArdCamera(); |
| | | updateCamera.setId(cameraId); |
| | | |
| | | if (name != null && !name.isEmpty()) { |
| | | updateCamera.setName(name); |
| | | } else { |
| | | updateCamera.setName(camera.getName()); |
| | | } |
| | | |
| | | if (ip != null && !ip.isEmpty()) { |
| | | updateCamera.setIp(ip); |
| | | } else { |
| | | updateCamera.setIp(camera.getIp()); |
| | | } |
| | | |
| | | if (state != null && !state.isEmpty()) { |
| | | updateCamera.setState(state); |
| | | } |
| | | |
| | | if (type != null && !type.isEmpty()) { |
| | | updateCamera.setType(type); |
| | | } |
| | | |
| | | if (factory != null && !factory.isEmpty()) { |
| | | updateCamera.setFactory(factory); |
| | | } |
| | | |
| | | // 3. 调用更新接口(假设有 updateCamera 方法) |
| | | R<Boolean> updateResult = remoteCameraService.updateCamera(updateCamera, SecurityConstants.INNER); |
| | | |
| | | if (updateResult == null) { |
| | | return "错误:服务返回空结果"; |
| | | } |
| | | |
| | | if (updateResult.getCode() == R.SUCCESS) { |
| | | // 返回更新后的信息 |
| | | // 4. 返回最新信息 |
| | | R<ArdCamera> newResult = remoteCameraService.getCameraInfoRpc(cameraId, SecurityConstants.INNER); |
| | | ArdCamera newCamera = newResult.getData(); |
| | | return String.format("摄像机信息修改成功!\n更新后的信息:\n" + |
| | | " - ID: %s\n" + |
| | | " - 名称: %s\n" + |
| | | " - IP地址: %s\n" + |
| | | " - 状态: %s\n" + |
| | | " - 类型: %s\n" + |
| | | " - 厂家: %s", |
| | | return String.format("✅ 摄像机信息修改成功!\n\n" + |
| | | "| 字段 | 修改后的值 |\n" + |
| | | "|------|-----------|\n" + |
| | | "| ID | %s |\n" + |
| | | "| 名称 | %s |\n" + |
| | | "| IP | %s |\n" + |
| | | "| 位置 | %s, %s |\n" + |
| | | "| 状态 | %s |\n" + |
| | | "| 类型 | %s |\n" + |
| | | "| 厂家 | %s |", |
| | | newCamera.getId(), |
| | | newCamera.getName(), |
| | | newCamera.getIp(), |
| | | Objects.equals(newCamera.getState(), "1") ? "在线" : "离线", |
| | | newCamera.getType(), |
| | | newCamera.getFactory()); |
| | | } else { |
| | | return String.format("修改摄像机信息失败:%s (code: %d)", |
| | | updateResult.getMsg(), updateResult.getCode()); |
| | | } |
| | | newCamera.getLongitude(), newCamera.getLatitude(), |
| | | "1".equals(newCamera.getState()) ? "在线" : "离线", |
| | | getCameraTypeName(newCamera.getType()), |
| | | getFactoryName(newCamera.getFactory())); |
| | | |
| | | } catch (Exception e) { |
| | | log.error("调用updateCamera异常", e); |