Merge pull request #2848 from xinnan-tech/perf-manager-api-device

perf:优化设备在线状态接口相关逻辑
This commit is contained in:
milieyo
2026-01-16 17:13:30 +08:00
committed by GitHub
11 changed files with 230 additions and 164 deletions
@@ -166,4 +166,14 @@ public class RedisKeys {
public static String getTmpRegisterMacKey(String deviceId) {
return "tmp_register_mac:" + deviceId;
}
/**
* OTA绑定设备
*/
public static String getOtaActivationCode(String activationCode) {return "ota:activation:code:" + activationCode;}
/**
* OTA获取设备mac相关信息
*/
public static String getOtaDeviceActivationInfo(String deviceId) {return "ota:activation:data:" + deviceId;}
}
@@ -0,0 +1,89 @@
package xiaozhi.common.utils;
import cn.hutool.core.util.ReUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 通用工具类
*/
public class ToolUtil {
private static final Logger logger = LoggerFactory.getLogger(ToolUtil.class);
/**
* 对象是否不为空(新增)
*/
public static boolean isNotEmpty(Object o) {
return !isEmpty(o);
}
/**
* 对象是否为空
*/
public static boolean isEmpty(Object o) {
if (o == null) {
return true;
}
if (o instanceof String) {
if (o.toString().trim().equals("")) {
return true;
}
} else if (o instanceof List) {
if (((List) o).size() == 0) {
return true;
}
} else if (o instanceof Map) {
if (((Map) o).size() == 0) {
return true;
}
} else if (o instanceof Set) {
if (((Set) o).size() == 0) {
return true;
}
} else if (o instanceof Object[]) {
if (((Object[]) o).length == 0) {
return true;
}
} else if (o instanceof int[]) {
if (((int[]) o).length == 0) {
return true;
}
} else if (o instanceof long[]) {
if (((long[]) o).length == 0) {
return true;
}
}
return false;
}
/**
* 对象组中是否存在空对象
*/
public static boolean isOneEmpty(Object... os) {
for (Object o : os) {
if (isEmpty(o)) {
return true;
}
}
return false;
}
/**
* 对象组中是否全是空对象
*/
public static boolean isAllEmpty(Object... os) {
for (Object o : os) {
if (!isEmpty(o)) {
return false;
}
}
return true;
}
}
@@ -28,7 +28,7 @@ public class AgentMcpAccessPointController {
/**
* 获取智能体的Mcp接入点地址
*
* @param audioId 智能体id
* @param agentId 智能体id
* @return 返回错误提醒或者Mcp接入点地址
*/
@Operation(summary = "获取智能体的Mcp接入点地址")
@@ -5,6 +5,7 @@ import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import cn.hutool.core.collection.ListUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -18,6 +19,7 @@ import xiaozhi.common.constant.Constant;
import xiaozhi.common.page.PageData;
import xiaozhi.common.utils.ConvertUtils;
import xiaozhi.common.utils.JsonUtils;
import xiaozhi.common.utils.ToolUtil;
import xiaozhi.modules.agent.Enums.AgentChatHistoryType;
import xiaozhi.modules.agent.dao.AiAgentChatHistoryDao;
import xiaozhi.modules.agent.dto.AgentChatHistoryDTO;
@@ -86,13 +88,12 @@ public class AgentChatHistoryServiceImpl extends ServiceImpl<AiAgentChatHistoryD
if (deleteAudio) {
// 分批删除音频,避免超时
List<String> audioIds = baseMapper.getAudioIdsByAgentId(agentId);
if (audioIds != null && !audioIds.isEmpty()) {
int batchSize = 1000; // 每批删除1000条
for (int i = 0; i < audioIds.size(); i += batchSize) {
int end = Math.min(i + batchSize, audioIds.size());
List<String> batch = audioIds.subList(i, end);
baseMapper.deleteAudioByIds(batch);
}
if (ToolUtil.isNotEmpty(audioIds)) {
// 每批删除1000条
List<List<String>> batch = ListUtil.split(audioIds, 1000);
batch.forEach(dataList->{
baseMapper.deleteAudioByIds(dataList);
});
}
}
if (deleteAudio && !deleteText) {
@@ -78,11 +78,11 @@ public class AgentChatSummaryServiceImpl implements AgentChatSummaryService {
// 4. 生成总结(generateSummaryFromMessages方法已包含长度限制逻辑)
String summary = generateSummaryFromMessages(meaningfulMessages, agentId);
System.out.println("成功生成会话 " + sessionId + " 的聊天记录总结,长度: " + summary.length() + " 字符");
log.info("成功生成会话 {} 的聊天记录总结,长度: {} 字符", sessionId, summary.length());
return new AgentChatSummaryDTO(sessionId, agentId, summary);
} catch (Exception e) {
System.err.println("生成会话 " + sessionId + " 的聊天记录总结时发生错误: " + e.getMessage());
log.error("生成会话 {} 的聊天记录总结时发生错误: {}", sessionId, e.getMessage());
return new AgentChatSummaryDTO(sessionId, "生成总结时发生错误: " + e.getMessage());
}
}
@@ -93,14 +93,14 @@ public class AgentChatSummaryServiceImpl implements AgentChatSummaryService {
// 1. 生成总结
AgentChatSummaryDTO summaryDTO = generateChatSummary(sessionId);
if (!summaryDTO.isSuccess()) {
System.err.println("生成总结失败: " + summaryDTO.getErrorMessage());
log.info("生成总结失败: {}", summaryDTO.getErrorMessage());
return false;
}
// 2. 获取设备信息(通过会话关联的设备)
DeviceEntity device = getDeviceBySessionId(sessionId);
if (device == null) {
System.err.println("未找到与会话 " + sessionId + " 关联的设备");
log.info("未找到与会话 {} 关联的设备", sessionId);
return false;
}
@@ -116,11 +116,11 @@ public class AgentChatSummaryServiceImpl implements AgentChatSummaryService {
}
});
System.out.println("成功保存会话 " + sessionId + " 的聊天记录总结到智能体 " + device.getAgentId());
log.info("成功保存会话 {} 的聊天记录总结到智能体 {}", sessionId, device.getAgentId());
return true;
} catch (Exception e) {
System.err.println("保存会话 " + sessionId + " 的聊天记录总结时发生错误: " + e.getMessage());
log.error("保存会话 {} 的聊天记录总结时发生错误: {}", sessionId, e.getMessage());
return false;
}
}
@@ -138,7 +138,7 @@ public class AgentChatSummaryServiceImpl implements AgentChatSummaryService {
}
return agentChatHistoryService.getChatHistoryBySessionId(agentId, sessionId);
} catch (Exception e) {
System.err.println("获取会话 " + sessionId + " 的聊天记录失败: " + e.getMessage());
log.error("获取会话 {} 的聊天记录失败: {}", sessionId, e.getMessage());
return null;
}
}
@@ -157,7 +157,7 @@ public class AgentChatSummaryServiceImpl implements AgentChatSummaryService {
AgentChatHistoryEntity entity = agentChatHistoryService.getOne(wrapper);
return entity != null ? entity.getAgentId() : null;
} catch (Exception e) {
System.err.println("根据会话ID " + sessionId + " 查找智能体ID失败: " + e.getMessage());
log.error("根据会话ID {} 查找智能体ID失败: {}", sessionId, e.getMessage());
return null;
}
}
@@ -272,7 +272,7 @@ public class AgentChatSummaryServiceImpl implements AgentChatSummaryService {
return summary;
} catch (Exception e) {
System.err.println("调用Java端LLM服务失败: " + e.getMessage());
log.error("调用Java端LLM服务失败: {}", e.getMessage());
throw new RuntimeException("LLM服务不可用,无法生成聊天总结");
}
}
@@ -295,7 +295,7 @@ public class AgentChatSummaryServiceImpl implements AgentChatSummaryService {
// 返回智能体的当前总结记忆
return agentInfo.getSummaryMemory();
} catch (Exception e) {
System.err.println("获取智能体历史记忆失败,agentId: " + agentId + ", 错误: " + e.getMessage());
log.error("获取智能体历史记忆失败,agentId: {}, 错误: {}", agentId, e.getMessage());
return null;
}
}
@@ -309,7 +309,7 @@ public class AgentChatSummaryServiceImpl implements AgentChatSummaryService {
String modelId = getMemorySummaryModelId(agentId);
if (StringUtils.isBlank(modelId)) {
System.out.println("未找到记忆总结的LLM模型配置,使用默认LLM服务");
log.info("未找到记忆总结的LLM模型配置,使用默认LLM服务");
return llmService.generateSummaryWithHistory(conversation, historyMemory, null, null);
}
@@ -323,7 +323,7 @@ public class AgentChatSummaryServiceImpl implements AgentChatSummaryService {
throw new RuntimeException("Java端LLM服务返回异常: " + summary);
} catch (Exception e) {
System.err.println("调用Java端LLM服务异常,agentId: " + agentId + ", 错误: " + e.getMessage());
log.error("调用Java端LLM服务异常,agentId: {}, 错误: {}", agentId, e.getMessage());
throw e;
}
}
@@ -337,7 +337,7 @@ public class AgentChatSummaryServiceImpl implements AgentChatSummaryService {
String modelId = getMemorySummaryModelId(agentId);
if (StringUtils.isBlank(modelId)) {
System.out.println("未找到记忆总结的LLM模型配置,使用默认LLM服务");
log.info("未找到记忆总结的LLM模型配置,使用默认LLM服务");
return llmService.generateSummary(conversation);
}
@@ -351,7 +351,7 @@ public class AgentChatSummaryServiceImpl implements AgentChatSummaryService {
throw new RuntimeException("Java端LLM服务返回异常: " + summary);
} catch (Exception e) {
System.err.println("调用Java端LLM服务异常,agentId: " + agentId + ", 错误: " + e.getMessage());
log.error("调用Java端LLM服务异常,agentId: {}, 错误: {}", agentId, e.getMessage());
throw e;
}
}
@@ -394,7 +394,7 @@ public class AgentChatSummaryServiceImpl implements AgentChatSummaryService {
return llmModelId;
} catch (Exception e) {
System.err.println("获取记忆总结LLM模型ID失败,agentId: " + agentId + ", 错误: " + e.getMessage());
log.error("获取记忆总结LLM模型ID失败,agentId: {}, 错误: {}", agentId, e.getMessage());
return null;
}
}
@@ -416,7 +416,7 @@ public class AgentChatSummaryServiceImpl implements AgentChatSummaryService {
}
return null;
} catch (Exception e) {
System.err.println("根据会话ID " + sessionId + " 查找设备信息失败: " + e.getMessage());
log.error("根据会话ID {} 查找设备信息失败: {}", sessionId, e.getMessage());
return null;
}
}
@@ -1,11 +1,6 @@
package xiaozhi.modules.agent.service.impl;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -28,6 +23,7 @@ import xiaozhi.common.service.impl.BaseServiceImpl;
import xiaozhi.common.user.UserDetail;
import xiaozhi.common.utils.ConvertUtils;
import xiaozhi.common.utils.JsonUtils;
import xiaozhi.common.utils.ToolUtil;
import xiaozhi.modules.agent.dao.AgentDao;
import xiaozhi.modules.agent.dto.AgentCreateDTO;
import xiaozhi.modules.agent.dto.AgentDTO;
@@ -136,20 +132,14 @@ public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> imp
if (StringUtils.isNotBlank(keyword)) {
if ("mac".equals(searchType)) {
// 按MAC地址搜索:先搜索设备,再获取对应的智能体
List<DeviceEntity> devices = deviceService.searchDevicesByMacAddress(keyword, userId);
if (!devices.isEmpty()) {
// 获取设备对应的智能体ID列表
List<String> agentIds = devices.stream()
.map(DeviceEntity::getAgentId)
.distinct()
.collect(Collectors.toList());
if (!agentIds.isEmpty()) {
queryWrapper.in("id", agentIds);
} else {
return new ArrayList<>();
}
List<DeviceEntity> devices = Optional.ofNullable(deviceService.searchDevicesByMacAddress(keyword, userId)).orElseGet(ArrayList::new);
// 获取设备对应的智能体ID列表
List<String> agentIds = devices.stream()
.map(DeviceEntity::getAgentId)
.distinct()
.collect(Collectors.toList());
if (ToolUtil.isNotEmpty(agentIds)) {
queryWrapper.in("id", agentIds);
} else {
return new ArrayList<>();
}
@@ -163,11 +163,11 @@ public class ConfigServiceImpl implements ConfigService {
}
result.put("chat_history_conf", chatHistoryConf);
// 如果客户端已实例化模型,则不返回
String alreadySelectedVadModelId = (String) selectedModule.get("VAD");
String alreadySelectedVadModelId = selectedModule.get("VAD");
if (alreadySelectedVadModelId != null && alreadySelectedVadModelId.equals(agent.getVadModelId())) {
agent.setVadModelId(null);
}
String alreadySelectedAsrModelId = (String) selectedModule.get("ASR");
String alreadySelectedAsrModelId = selectedModule.get("ASR");
if (alreadySelectedAsrModelId != null && alreadySelectedAsrModelId.equals(agent.getAsrModelId())) {
agent.setAsrModelId(null);
}
@@ -302,7 +302,7 @@ public class ConfigServiceImpl implements ConfigService {
private void buildVoiceprintConfig(String agentId, Map<String, Object> result) {
try {
// 获取声纹接口地址
String voiceprintUrl = sysParamsService.getValue("server.voice_print", true);
String voiceprintUrl = sysParamsService.getValue(Constant.SERVER_VOICE_PRINT, true);
if (StringUtils.isBlank(voiceprintUrl) || "null".equals(voiceprintUrl)) {
return;
}
@@ -1,14 +1,9 @@
package xiaozhi.modules.device.controller;
import java.util.List;
import java.util.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.BeanUtils;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
@@ -16,10 +11,6 @@ import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
@@ -44,16 +35,11 @@ public class DeviceController {
private final DeviceService deviceService;
private final RedisUtils redisUtils;
private final SysParamsService sysParamsService;
private final RestTemplate restTemplate;
private final ObjectMapper objectMapper;
public DeviceController(DeviceService deviceService, RedisUtils redisUtils, SysParamsService sysParamsService,
RestTemplate restTemplate, ObjectMapper objectMapper) {
public DeviceController(DeviceService deviceService, RedisUtils redisUtils, SysParamsService sysParamsService) {
this.deviceService = deviceService;
this.redisUtils = redisUtils;
this.sysParamsService = sysParamsService;
this.restTemplate = restTemplate;
this.objectMapper = objectMapper;
}
@PostMapping("/bind/{agentId}/{deviceCode}")
@@ -99,83 +85,12 @@ public class DeviceController {
@RequiresPermissions("sys:role:normal")
public Result<String> forwardToMqttGateway(@PathVariable String agentId, @RequestBody String requestBody) {
try {
// 从系统参数中获取MQTT网关地址
String mqttGatewayUrl = sysParamsService.getValue("server.mqtt_manager_api", true);
if (StringUtils.isBlank(mqttGatewayUrl) || "null".equals(mqttGatewayUrl)) {
return new Result<>();
}
// 获取当前用户的设备列表
UserDetail user = SecurityUser.getUser();
List<DeviceEntity> devices = deviceService.getUserDevices(user.getId(), agentId);
// 构建deviceIds数组
java.util.List<String> deviceIds = new java.util.ArrayList<>();
for (DeviceEntity device : devices) {
String macAddress = device.getMacAddress() != null ? device.getMacAddress() : "unknown";
String groupId = device.getBoard() != null ? device.getBoard() : "GID_default";
// 替换冒号为下划线
groupId = groupId.replace(":", "_");
macAddress = macAddress.replace(":", "_");
// 构建mqtt客户端ID格式:groupId@@@macAddress@@@macAddress
String mqttClientId = groupId + "@@@" + macAddress + "@@@" + macAddress;
deviceIds.add(mqttClientId);
}
// 构建完整的URL
String url = "http://" + mqttGatewayUrl + "/api/devices/status";
// 设置请求头
HttpHeaders headers = new HttpHeaders();
headers.set("Content-Type", "application/json");
// 生成Bearer令牌
String token = generateBearerToken();
if (token == null) {
return new Result<String>().error("令牌生成失败");
}
headers.set("Authorization", "Bearer " + token);
// 构建请求体JSON
String jsonBody = "{\"clientIds\":" + objectMapper.writeValueAsString(deviceIds) + "}";
HttpEntity<String> requestEntity = new HttpEntity<>(jsonBody, headers);
// 发送POST请求
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
// 返回响应
return new Result<String>().ok(response.getBody());
return new Result<String>().ok(deviceService.getDeviceOnlineData(agentId));
} catch (Exception e) {
return new Result<String>().error("转发请求失败: " + e.getMessage());
}
}
private String generateBearerToken() {
try {
// 获取当前日期,格式为yyyy-MM-dd
String dateStr = java.time.LocalDate.now()
.format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd"));
// 获取MQTT签名密钥
String signatureKey = sysParamsService.getValue("server.mqtt_signature_key", false);
if (StringUtils.isBlank(signatureKey)) {
return null;
}
// 将日期字符串与MQTT_SIGNATURE_KEY连接
String tokenContent = dateStr + signatureKey;
// 对连接后的字符串进行SHA256哈希计算
String token = org.apache.commons.codec.digest.DigestUtils.sha256Hex(tokenContent);
return token;
} catch (Exception e) {
return null;
}
}
@PostMapping("/unbind")
@Operation(summary = "解绑设备")
@RequiresPermissions("sys:role:normal")
@@ -7,7 +7,9 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
@@ -145,15 +147,11 @@ public class OTAMagController {
// 检查下载次数
String downloadCountKey = RedisKeys.getOtaDownloadCountKey(uuid);
Integer downloadCount = (Integer) redisUtils.get(downloadCountKey);
if (downloadCount == null) {
downloadCount = 0;
}
Integer downloadCount = (Integer) Optional.ofNullable(redisUtils.get(downloadCountKey)).orElse(0);
// 如果下载次数超过3次,返回404
if (downloadCount >= 3) {
redisUtils.delete(downloadCountKey);
redisUtils.delete(RedisKeys.getOtaIdKey(uuid));
redisUtils.delete(List.of(downloadCountKey, RedisKeys.getOtaIdKey(uuid)));
logger.warn("Download limit exceeded for UUID: {}", uuid);
return ResponseEntity.notFound().build();
}
@@ -13,6 +13,10 @@ import xiaozhi.modules.device.entity.DeviceEntity;
import xiaozhi.modules.device.vo.UserShowDeviceListVO;
public interface DeviceService extends BaseService<DeviceEntity> {
/**
* 获取设备在线数据
*/
String getDeviceOnlineData(String agentId);
/**
* 检查设备是否激活
@@ -4,17 +4,21 @@ import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.util.UUID;
import java.util.*;
import java.util.stream.Collectors;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.http.ContentType;
import cn.hutool.http.Header;
import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.aop.framework.AopContext;
import org.springframework.scheduling.annotation.Async;
@@ -40,6 +44,8 @@ import xiaozhi.common.service.impl.BaseServiceImpl;
import xiaozhi.common.user.UserDetail;
import xiaozhi.common.utils.ConvertUtils;
import xiaozhi.common.utils.DateUtils;
import xiaozhi.common.utils.Result;
import xiaozhi.common.utils.ToolUtil;
import xiaozhi.modules.device.dao.DeviceDao;
import xiaozhi.modules.device.dto.DeviceManualAddDTO;
import xiaozhi.modules.device.dto.DevicePageUserDTO;
@@ -88,16 +94,16 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
if (StringUtils.isBlank(activationCode)) {
throw new RenException(ErrorCode.ACTIVATION_CODE_EMPTY);
}
String deviceKey = "ota:activation:code:" + activationCode;
String deviceKey = RedisKeys.getOtaActivationCode(activationCode);
Object cacheDeviceId = redisUtils.get(deviceKey);
if (cacheDeviceId == null) {
if (ToolUtil.isEmpty(cacheDeviceId)) {
throw new RenException(ErrorCode.ACTIVATION_CODE_ERROR);
}
String deviceId = (String) cacheDeviceId;
String safeDeviceId = deviceId.replace(":", "_").toLowerCase();
String cacheDeviceKey = String.format("ota:activation:data:%s", safeDeviceId);
String cacheDeviceKey = RedisKeys.getOtaDeviceActivationInfo(safeDeviceId);
Map<String, Object> cacheMap = (Map<String, Object>) redisUtils.get(cacheDeviceKey);
if (cacheMap == null) {
if (ToolUtil.isEmpty(cacheMap)) {
throw new RenException(ErrorCode.ACTIVATION_CODE_ERROR);
}
String cachedCode = (String) cacheMap.get("activation_code");
@@ -133,19 +139,56 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
deviceEntity.setLastConnectedAt(currentTime);
deviceDao.insert(deviceEntity);
// 清理redis缓存
redisUtils.delete(cacheDeviceKey);
redisUtils.delete(deviceKey);
// 添加:清除智能体设备数量缓存
redisUtils.delete(RedisKeys.getAgentDeviceCountById(agentId));
// 清理redis缓存、清除智能体设备数量缓存
redisUtils.delete(List.of(cacheDeviceKey, deviceKey, RedisKeys.getAgentDeviceCountById(agentId)));
return true;
}
/**
* 获取设备在线数据
*/
@Override
public DeviceReportRespDTO checkDeviceActive(String macAddress, String clientId,
DeviceReportReqDTO deviceReport) {
public String getDeviceOnlineData(String agentId) {
// 从系统参数中获取MQTT网关地址
String mqttGatewayUrl = sysParamsService.getValue("server.mqtt_manager_api", true);
if (StringUtils.isBlank(mqttGatewayUrl) || "null".equals(mqttGatewayUrl)) {
return "";
}
// 构建完整的URL
String url = StrUtil.format("http://{}/api/devices/status", mqttGatewayUrl);
// 获取当前用户的设备列表
UserDetail user = SecurityUser.getUser();
List<DeviceEntity> devices = getUserDevices(user.getId(), agentId);
// 构建deviceIds数组
Set<String> deviceIds = devices.stream().map(o -> {
String macAddress = Optional.ofNullable(o.getMacAddress()).orElse("unknown").replace(":", "_");
String groupId = Optional.ofNullable(o.getBoard()).orElse("GID_default").replace(":", "_");
return StrUtil.format("{}@@@{}@@@{}", groupId, macAddress, macAddress);
}).collect(Collectors.toSet());
// 构建请求入参
Map<String, Set<String>> params = MapUtil
.builder(new HashMap<String, Set<String>>())
.put("clientIds", deviceIds).build();
if (ToolUtil.isNotEmpty(deviceIds)) {
// 发送请求
String resultMessage = HttpRequest.post(url)
.header(Header.CONTENT_TYPE, ContentType.JSON.getValue())
.header(Header.AUTHORIZATION, "Bearer " + generateBearerToken())
.body(JSONUtil.toJsonStr(params))
.timeout(10000) //超时,毫秒
.execute().body();
return resultMessage;
}
// 返回响应
return "";
}
@Override
public DeviceReportRespDTO checkDeviceActive(String macAddress, String clientId, DeviceReportReqDTO deviceReport) {
DeviceReportRespDTO response = new DeviceReportRespDTO();
response.setServer_time(buildServerTime());
@@ -356,8 +399,7 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
private String getDeviceCacheKey(String deviceId) {
String safeDeviceId = deviceId.replace(":", "_").toLowerCase();
String dataKey = String.format("ota:activation:data:%s", safeDeviceId);
return dataKey;
return RedisKeys.getOtaDeviceActivationInfo(safeDeviceId);
}
public DeviceReportRespDTO.Activation buildActivation(String deviceId, DeviceReportReqDTO deviceReport) {
@@ -396,7 +438,7 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
redisUtils.set(dataKey, dataMap);
// 写入反查激活码 key
String codeKey = "ota:activation:code:" + newCode;
String codeKey = RedisKeys.getOtaActivationCode(newCode);
redisUtils.set(codeKey, deviceId);
}
return code;
@@ -606,4 +648,21 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
return mqtt;
}
/**
* 生成BearerToken
*/
private String generateBearerToken() {
try {
String dateStr = DateUtil.format(new Date(), DatePattern.NORM_DATE_PATTERN);
String signatureKey = sysParamsService.getValue(Constant.SERVER_MQTT_SECRET,false);
if (ToolUtil.isEmpty(signatureKey)) {
return null;
}
return DigestUtil.sha256Hex(dateStr + signatureKey);
} catch (Exception e) {
return null;
}
}
}