mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-25 00:23:53 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a2c90ec87 | ||
|
|
27e57631a7 | ||
|
|
b1e39da9b8 | ||
|
|
e87fc766d2 |
@@ -21,14 +21,14 @@
|
|||||||
<junit.version>5.10.1</junit.version>
|
<junit.version>5.10.1</junit.version>
|
||||||
<druid.version>1.2.20</druid.version>
|
<druid.version>1.2.20</druid.version>
|
||||||
<mybatisplus.version>3.5.17</mybatisplus.version>
|
<mybatisplus.version>3.5.17</mybatisplus.version>
|
||||||
<hutool.version>5.8.24</hutool.version>
|
<hutool.version>5.8.46</hutool.version>
|
||||||
<jsoup.version>1.19.1</jsoup.version>
|
<jsoup.version>1.22.2</jsoup.version>
|
||||||
<knife4j.version>4.6.0</knife4j.version>
|
<knife4j.version>4.6.0</knife4j.version>
|
||||||
<springdoc.version>2.8.8</springdoc.version>
|
<springdoc.version>2.8.8</springdoc.version>
|
||||||
<commons-lang3.version>3.18.0</commons-lang3.version>
|
<commons-lang3.version>3.20.0</commons-lang3.version>
|
||||||
<shiro.version>2.0.2</shiro.version>
|
<shiro.version>2.0.2</shiro.version>
|
||||||
<captcha.version>1.6.2</captcha.version>
|
<captcha.version>1.6.2</captcha.version>
|
||||||
<guava.version>33.0.0-jre</guava.version>
|
<guava.version>33.6.0-jre</guava.version>
|
||||||
<liquibase-core.version>4.20.0</liquibase-core.version>
|
<liquibase-core.version>4.20.0</liquibase-core.version>
|
||||||
<aliyun-sms-version>4.1.0</aliyun-sms-version>
|
<aliyun-sms-version>4.1.0</aliyun-sms-version>
|
||||||
<okio-version>3.4.0</okio-version>
|
<okio-version>3.4.0</okio-version>
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
package xiaozhi.common.utils;
|
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 哈希加密算法的工具类
|
|
||||||
* @author zjy
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
public class HashEncryptionUtil {
|
|
||||||
/**
|
|
||||||
* 使用md5进行加密
|
|
||||||
* @param context 被加密的内容
|
|
||||||
* @return 哈希值
|
|
||||||
*/
|
|
||||||
public static String Md5hexDigest(String context){
|
|
||||||
return hexDigest(context,"MD5");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 指定哈希算法进行加密
|
|
||||||
* @param context 被加密的内容
|
|
||||||
* @param algorithm 哈希算法
|
|
||||||
* @return 哈希值
|
|
||||||
*/
|
|
||||||
public static String hexDigest(String context,String algorithm ){
|
|
||||||
// 获取MD5算法实例
|
|
||||||
MessageDigest md = null;
|
|
||||||
try {
|
|
||||||
md = MessageDigest.getInstance(algorithm);
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
log.error("加密失败的算法:{}",algorithm);
|
|
||||||
throw new RuntimeException("加密失败,"+ algorithm +"哈希算法系统不支持");
|
|
||||||
}
|
|
||||||
// 计算智能体id的MD5值
|
|
||||||
byte[] messageDigest = md.digest(context.getBytes());
|
|
||||||
// 将字节数组转换为十六进制字符串
|
|
||||||
StringBuilder hexString = new StringBuilder();
|
|
||||||
for (byte b : messageDigest) {
|
|
||||||
String hex = Integer.toHexString(0xFF & b);
|
|
||||||
if (hex.length() == 1) {
|
|
||||||
hexString.append('0');
|
|
||||||
}
|
|
||||||
hexString.append(hex);
|
|
||||||
}
|
|
||||||
return hexString.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+2
-2
@@ -5,6 +5,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.collection.ListUtil;
|
import cn.hutool.core.collection.ListUtil;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -20,7 +21,6 @@ import xiaozhi.common.constant.Constant;
|
|||||||
import xiaozhi.common.page.PageData;
|
import xiaozhi.common.page.PageData;
|
||||||
import xiaozhi.common.utils.ConvertUtils;
|
import xiaozhi.common.utils.ConvertUtils;
|
||||||
import xiaozhi.common.utils.JsonUtils;
|
import xiaozhi.common.utils.JsonUtils;
|
||||||
import xiaozhi.common.utils.ToolUtil;
|
|
||||||
import xiaozhi.modules.agent.Enums.AgentChatHistoryType;
|
import xiaozhi.modules.agent.Enums.AgentChatHistoryType;
|
||||||
import xiaozhi.modules.agent.dao.AiAgentChatHistoryDao;
|
import xiaozhi.modules.agent.dao.AiAgentChatHistoryDao;
|
||||||
import xiaozhi.modules.agent.dto.AgentChatHistoryDTO;
|
import xiaozhi.modules.agent.dto.AgentChatHistoryDTO;
|
||||||
@@ -107,7 +107,7 @@ public class AgentChatHistoryServiceImpl extends CrudRepository<AiAgentChatHisto
|
|||||||
if (deleteAudio) {
|
if (deleteAudio) {
|
||||||
// 分批删除音频,避免超时
|
// 分批删除音频,避免超时
|
||||||
List<String> audioIds = baseMapper.getAudioIdsByAgentId(agentId);
|
List<String> audioIds = baseMapper.getAudioIdsByAgentId(agentId);
|
||||||
if (ToolUtil.isNotEmpty(audioIds)) {
|
if (CollUtil.isNotEmpty(audioIds)) {
|
||||||
// 每批删除1000条
|
// 每批删除1000条
|
||||||
List<List<String>> batch = ListUtil.split(audioIds, 1000);
|
List<List<String>> batch = ListUtil.split(audioIds, 1000);
|
||||||
batch.forEach(dataList -> {
|
batch.forEach(dataList -> {
|
||||||
|
|||||||
+2
-2
@@ -12,11 +12,11 @@ import java.util.stream.Collectors;
|
|||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import cn.hutool.crypto.digest.DigestUtil;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import xiaozhi.common.constant.Constant;
|
import xiaozhi.common.constant.Constant;
|
||||||
import xiaozhi.common.utils.AESUtils;
|
import xiaozhi.common.utils.AESUtils;
|
||||||
import xiaozhi.common.utils.HashEncryptionUtil;
|
|
||||||
import xiaozhi.common.utils.JsonUtils;
|
import xiaozhi.common.utils.JsonUtils;
|
||||||
import xiaozhi.modules.agent.Enums.XiaoZhiMcpJsonRpcJson;
|
import xiaozhi.modules.agent.Enums.XiaoZhiMcpJsonRpcJson;
|
||||||
import xiaozhi.modules.agent.service.AgentMcpAccessPointService;
|
import xiaozhi.modules.agent.service.AgentMcpAccessPointService;
|
||||||
@@ -226,7 +226,7 @@ public class AgentMcpAccessPointServiceImpl implements AgentMcpAccessPointServic
|
|||||||
*/
|
*/
|
||||||
private static String encryptToken(String agentId, String key) {
|
private static String encryptToken(String agentId, String key) {
|
||||||
// 使用md5对智能体id进行加密
|
// 使用md5对智能体id进行加密
|
||||||
String md5 = HashEncryptionUtil.Md5hexDigest(agentId);
|
String md5 = DigestUtil.md5Hex(agentId);
|
||||||
// aes需要加密文本
|
// aes需要加密文本
|
||||||
String json = "{\"agentId\": \"%s\"}".formatted(md5);
|
String json = "{\"agentId\": \"%s\"}".formatted(md5);
|
||||||
// 加密后成token值
|
// 加密后成token值
|
||||||
|
|||||||
+4
-4
@@ -18,6 +18,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.baomidou.mybatisplus.extension.repository.IRepository;
|
import com.baomidou.mybatisplus.extension.repository.IRepository;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import xiaozhi.common.constant.Constant;
|
import xiaozhi.common.constant.Constant;
|
||||||
import xiaozhi.common.exception.ErrorCode;
|
import xiaozhi.common.exception.ErrorCode;
|
||||||
@@ -29,7 +30,6 @@ import xiaozhi.common.service.impl.BaseServiceImpl;
|
|||||||
import xiaozhi.common.user.UserDetail;
|
import xiaozhi.common.user.UserDetail;
|
||||||
import xiaozhi.common.utils.ConvertUtils;
|
import xiaozhi.common.utils.ConvertUtils;
|
||||||
import xiaozhi.common.utils.JsonUtils;
|
import xiaozhi.common.utils.JsonUtils;
|
||||||
import xiaozhi.common.utils.ToolUtil;
|
|
||||||
import xiaozhi.modules.agent.dao.AgentDao;
|
import xiaozhi.modules.agent.dao.AgentDao;
|
||||||
import xiaozhi.modules.agent.dao.AgentTagDao;
|
import xiaozhi.modules.agent.dao.AgentTagDao;
|
||||||
import xiaozhi.modules.agent.dto.AgentCreateDTO;
|
import xiaozhi.modules.agent.dto.AgentCreateDTO;
|
||||||
@@ -243,13 +243,13 @@ public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> imp
|
|||||||
.map(DeviceEntity::getAgentId)
|
.map(DeviceEntity::getAgentId)
|
||||||
.distinct()
|
.distinct()
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
if (ToolUtil.isNotEmpty(agentIds)) {
|
if (CollUtil.isNotEmpty(agentIds)) {
|
||||||
w.or().in("id", agentIds);
|
w.or().in("id", agentIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 按标签名搜索
|
// 按标签名搜索
|
||||||
List<String> tagAgentIds = agentTagService.getAgentIdsByTagName(keyword);
|
List<String> tagAgentIds = agentTagService.getAgentIdsByTagName(keyword);
|
||||||
if (ToolUtil.isNotEmpty(tagAgentIds)) {
|
if (CollUtil.isNotEmpty(tagAgentIds)) {
|
||||||
w.or().in("id", tagAgentIds);
|
w.or().in("id", tagAgentIds);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -291,7 +291,7 @@ public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> imp
|
|||||||
|
|
||||||
// 获取标签列表
|
// 获取标签列表
|
||||||
List<AgentTagEntity> tags = agentTagDao.selectByAgentId(agent.getId());
|
List<AgentTagEntity> tags = agentTagDao.selectByAgentId(agent.getId());
|
||||||
if (ToolUtil.isNotEmpty(tags)) {
|
if (CollUtil.isNotEmpty(tags)) {
|
||||||
dto.setTags(tags.stream().map(this::convertTagToDTO).collect(Collectors.toList()));
|
dto.setTags(tags.stream().map(this::convertTagToDTO).collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-2
@@ -125,7 +125,9 @@ public class DeviceController {
|
|||||||
return new Result<Void>().error("设备不存在");
|
return new Result<Void>().error("设备不存在");
|
||||||
}
|
}
|
||||||
BeanUtils.copyProperties(deviceUpdateDTO, entity);
|
BeanUtils.copyProperties(deviceUpdateDTO, entity);
|
||||||
deviceService.updateById(entity);
|
if (!deviceService.updateById(entity)) {
|
||||||
|
return new Result<Void>().error(ErrorCode.UPDATE_DATA_FAILED);
|
||||||
|
}
|
||||||
return new Result<Void>();
|
return new Result<Void>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,4 +213,4 @@ public class DeviceController {
|
|||||||
deviceAddressBookService.saveOrUpdate(dto.getMacAddress(), dto.getTargetMac(), null, dto.getHasPermission());
|
deviceAddressBookService.saveOrUpdate(dto.getMacAddress(), dto.getTargetMac(), null, dto.getHasPermission());
|
||||||
return new Result<Void>();
|
return new Result<Void>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-11
@@ -5,8 +5,6 @@ import java.io.IOException;
|
|||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -31,6 +29,7 @@ import org.springframework.web.bind.annotation.RequestParam;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import cn.hutool.crypto.digest.DigestUtil;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.Parameters;
|
import io.swagger.v3.oas.annotations.Parameters;
|
||||||
@@ -288,7 +287,7 @@ public class OTAMagController {
|
|||||||
|
|
||||||
// 返回文件路径
|
// 返回文件路径
|
||||||
return new Result<String>().ok(filePath.toString());
|
return new Result<String>().ok(filePath.toString());
|
||||||
} catch (IOException | NoSuchAlgorithmException e) {
|
} catch (IOException e) {
|
||||||
return new Result<String>().error("文件上传失败:" + e.getMessage());
|
return new Result<String>().error("文件上传失败:" + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -329,13 +328,7 @@ public class OTAMagController {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String calculateMD5(MultipartFile file) throws IOException, NoSuchAlgorithmException {
|
private String calculateMD5(MultipartFile file) throws IOException {
|
||||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
return DigestUtil.md5Hex(file.getBytes());
|
||||||
byte[] digest = md.digest(file.getBytes());
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
for (byte b : digest) {
|
|
||||||
sb.append(String.format("%02x", b));
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-8
@@ -31,6 +31,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|||||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.map.MapUtil;
|
import cn.hutool.core.map.MapUtil;
|
||||||
import cn.hutool.core.util.RandomUtil;
|
import cn.hutool.core.util.RandomUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
@@ -51,7 +52,6 @@ import xiaozhi.common.user.UserDetail;
|
|||||||
import xiaozhi.common.utils.ConvertUtils;
|
import xiaozhi.common.utils.ConvertUtils;
|
||||||
import xiaozhi.common.utils.DateUtils;
|
import xiaozhi.common.utils.DateUtils;
|
||||||
import xiaozhi.common.utils.JsonUtils;
|
import xiaozhi.common.utils.JsonUtils;
|
||||||
import xiaozhi.common.utils.ToolUtil;
|
|
||||||
import xiaozhi.modules.device.dao.DeviceDao;
|
import xiaozhi.modules.device.dao.DeviceDao;
|
||||||
import xiaozhi.modules.device.dto.DeviceManualAddDTO;
|
import xiaozhi.modules.device.dto.DeviceManualAddDTO;
|
||||||
import xiaozhi.modules.device.dto.DevicePageUserDTO;
|
import xiaozhi.modules.device.dto.DevicePageUserDTO;
|
||||||
@@ -103,15 +103,15 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
|
|||||||
throw new RenException(ErrorCode.ACTIVATION_CODE_EMPTY);
|
throw new RenException(ErrorCode.ACTIVATION_CODE_EMPTY);
|
||||||
}
|
}
|
||||||
String deviceKey = RedisKeys.getOtaActivationCode(activationCode);
|
String deviceKey = RedisKeys.getOtaActivationCode(activationCode);
|
||||||
Object cacheDeviceId = redisUtils.get(deviceKey);
|
String cacheDeviceId = (String) redisUtils.get(deviceKey);
|
||||||
if (ToolUtil.isEmpty(cacheDeviceId)) {
|
if (StringUtils.isBlank(cacheDeviceId)) {
|
||||||
throw new RenException(ErrorCode.ACTIVATION_CODE_ERROR);
|
throw new RenException(ErrorCode.ACTIVATION_CODE_ERROR);
|
||||||
}
|
}
|
||||||
String deviceId = (String) cacheDeviceId;
|
String deviceId = cacheDeviceId;
|
||||||
String safeDeviceId = deviceId.replace(":", "_").toLowerCase();
|
String safeDeviceId = deviceId.replace(":", "_").toLowerCase();
|
||||||
String cacheDeviceKey = RedisKeys.getOtaDeviceActivationInfo(safeDeviceId);
|
String cacheDeviceKey = RedisKeys.getOtaDeviceActivationInfo(safeDeviceId);
|
||||||
Map<String, Object> cacheMap = JsonUtils.toStringObjectMap(redisUtils.get(cacheDeviceKey));
|
Map<String, Object> cacheMap = JsonUtils.toStringObjectMap(redisUtils.get(cacheDeviceKey));
|
||||||
if (ToolUtil.isEmpty(cacheMap)) {
|
if (MapUtil.isEmpty(cacheMap)) {
|
||||||
throw new RenException(ErrorCode.ACTIVATION_CODE_ERROR);
|
throw new RenException(ErrorCode.ACTIVATION_CODE_ERROR);
|
||||||
}
|
}
|
||||||
String cachedCode = (String) cacheMap.get("activation_code");
|
String cachedCode = (String) cacheMap.get("activation_code");
|
||||||
@@ -181,7 +181,7 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
|
|||||||
.builder(new HashMap<String, Set<String>>())
|
.builder(new HashMap<String, Set<String>>())
|
||||||
.put("clientIds", deviceIds).build();
|
.put("clientIds", deviceIds).build();
|
||||||
|
|
||||||
if (ToolUtil.isNotEmpty(deviceIds)) {
|
if (CollUtil.isNotEmpty(deviceIds)) {
|
||||||
return postToMqttGateway(url, params);
|
return postToMqttGateway(url, params);
|
||||||
}
|
}
|
||||||
// 返回响应
|
// 返回响应
|
||||||
@@ -202,8 +202,8 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
|
|||||||
firmware.setUrl(Constant.INVALID_FIRMWARE_URL);
|
firmware.setUrl(Constant.INVALID_FIRMWARE_URL);
|
||||||
response.setFirmware(firmware);
|
response.setFirmware(firmware);
|
||||||
} else {
|
} else {
|
||||||
// 只有在设备已绑定且autoUpdate不为0的情况下才返回固件升级信息
|
// 只有在设备已绑定且明确开启自动升级时才返回固件升级信息
|
||||||
if (deviceById.getAutoUpdate() != 0) {
|
if (Integer.valueOf(1).equals(deviceById.getAutoUpdate())) {
|
||||||
String type = deviceReport.getBoard() == null ? null : deviceReport.getBoard().getType();
|
String type = deviceReport.getBoard() == null ? null : deviceReport.getBoard().getType();
|
||||||
DeviceReportRespDTO.Firmware firmware = buildFirmwareInfo(type,
|
DeviceReportRespDTO.Firmware firmware = buildFirmwareInfo(type,
|
||||||
deviceReport.getApplication() == null ? null : deviceReport.getApplication().getVersion());
|
deviceReport.getApplication() == null ? null : deviceReport.getApplication().getVersion());
|
||||||
@@ -299,6 +299,7 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
|
|||||||
UserShowDeviceListVO vo = ConvertUtils.sourceToTarget(device, UserShowDeviceListVO.class);
|
UserShowDeviceListVO vo = ConvertUtils.sourceToTarget(device, UserShowDeviceListVO.class);
|
||||||
vo.setDeviceType(device.getBoard());
|
vo.setDeviceType(device.getBoard());
|
||||||
vo.setBoard(device.getBoard());
|
vo.setBoard(device.getBoard());
|
||||||
|
vo.setAutoUpdate(device.getAutoUpdate());
|
||||||
vo.setCreateDateTimestamp(toTimestamp(device.getCreateDate()));
|
vo.setCreateDateTimestamp(toTimestamp(device.getCreateDate()));
|
||||||
vo.setLastConnectedAtTimestamp(toTimestamp(device.getLastConnectedAt()));
|
vo.setLastConnectedAtTimestamp(toTimestamp(device.getLastConnectedAt()));
|
||||||
return vo;
|
return vo;
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ public class UserShowDeviceListVO {
|
|||||||
@Schema(description = "设备别名")
|
@Schema(description = "设备别名")
|
||||||
private String alias;
|
private String alias;
|
||||||
|
|
||||||
@Schema(description = "开启OTA")
|
@Schema(description = "自动更新开关(0关闭/1开启)")
|
||||||
private Integer otaUpgrade;
|
private Integer autoUpdate;
|
||||||
|
|
||||||
@Schema(description = "最近对话时间")
|
@Schema(description = "最近对话时间")
|
||||||
private String recentChatTime;
|
private String recentChatTime;
|
||||||
|
|||||||
+3
-3
@@ -15,6 +15,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
@@ -23,7 +24,6 @@ import xiaozhi.common.exception.ErrorCode;
|
|||||||
import xiaozhi.common.exception.RenException;
|
import xiaozhi.common.exception.RenException;
|
||||||
import xiaozhi.common.page.PageData;
|
import xiaozhi.common.page.PageData;
|
||||||
import xiaozhi.common.utils.Result;
|
import xiaozhi.common.utils.Result;
|
||||||
import xiaozhi.common.utils.ToolUtil;
|
|
||||||
import xiaozhi.modules.knowledge.dto.KnowledgeBaseDTO;
|
import xiaozhi.modules.knowledge.dto.KnowledgeBaseDTO;
|
||||||
import xiaozhi.modules.knowledge.service.KnowledgeBaseService;
|
import xiaozhi.modules.knowledge.service.KnowledgeBaseService;
|
||||||
import xiaozhi.modules.knowledge.service.KnowledgeManagerService;
|
import xiaozhi.modules.knowledge.service.KnowledgeManagerService;
|
||||||
@@ -140,7 +140,7 @@ public class KnowledgeBaseController {
|
|||||||
List<String> idList = Arrays.asList(ids.split(","));
|
List<String> idList = Arrays.asList(ids.split(","));
|
||||||
List<KnowledgeBaseDTO> knowledgeBaseDTOs = Optional.ofNullable(knowledgeBaseService.getByDatasetIdList(idList))
|
List<KnowledgeBaseDTO> knowledgeBaseDTOs = Optional.ofNullable(knowledgeBaseService.getByDatasetIdList(idList))
|
||||||
.orElseGet(ArrayList::new);
|
.orElseGet(ArrayList::new);
|
||||||
if (ToolUtil.isNotEmpty(knowledgeBaseDTOs)) {
|
if (CollUtil.isNotEmpty(knowledgeBaseDTOs)) {
|
||||||
knowledgeBaseDTOs.forEach(item -> {
|
knowledgeBaseDTOs.forEach(item -> {
|
||||||
// 检查权限:用户只能删除自己创建的知识库
|
// 检查权限:用户只能删除自己创建的知识库
|
||||||
if (item.getCreator() == null || !item.getCreator().equals(currentUserId)) {
|
if (item.getCreator() == null || !item.getCreator().equals(currentUserId)) {
|
||||||
@@ -160,4 +160,4 @@ public class KnowledgeBaseController {
|
|||||||
List<ModelConfigEntity> result = knowledgeBaseService.getRAGModels();
|
List<ModelConfigEntity> result = knowledgeBaseService.getRAGModels();
|
||||||
return new Result<List<ModelConfigEntity>>().ok(result);
|
return new Result<List<ModelConfigEntity>>().ok(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-21
@@ -1,8 +1,7 @@
|
|||||||
package xiaozhi.modules.security.service.impl;
|
package xiaozhi.modules.security.service.impl;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Random;
|
import java.time.Duration;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
@@ -13,6 +12,7 @@ import com.google.common.cache.CacheBuilder;
|
|||||||
import com.wf.captcha.SpecCaptcha;
|
import com.wf.captcha.SpecCaptcha;
|
||||||
import com.wf.captcha.base.Captcha;
|
import com.wf.captcha.base.Captcha;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.RandomUtil;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import xiaozhi.common.constant.Constant;
|
import xiaozhi.common.constant.Constant;
|
||||||
@@ -41,7 +41,7 @@ public class CaptchaServiceImpl implements CaptchaService {
|
|||||||
* Local Cache 5分钟过期
|
* Local Cache 5分钟过期
|
||||||
*/
|
*/
|
||||||
Cache<String, String> localCache = CacheBuilder.newBuilder().maximumSize(1000)
|
Cache<String, String> localCache = CacheBuilder.newBuilder().maximumSize(1000)
|
||||||
.expireAfterAccess(5, TimeUnit.MINUTES).build();
|
.expireAfterAccess(Duration.ofMinutes(5)).build();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void create(HttpServletResponse response, String uuid) throws IOException {
|
public void create(HttpServletResponse response, String uuid) throws IOException {
|
||||||
@@ -113,7 +113,7 @@ public class CaptchaServiceImpl implements CaptchaService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String key = RedisKeys.getSMSValidateCodeKey(phone);
|
String key = RedisKeys.getSMSValidateCodeKey(phone);
|
||||||
String validateCodes = generateValidateCode(6);
|
String validateCodes = RandomUtil.randomNumbers(6);
|
||||||
|
|
||||||
// 设置验证码
|
// 设置验证码
|
||||||
setCache(key, validateCodes);
|
setCache(key, validateCodes);
|
||||||
@@ -135,22 +135,6 @@ public class CaptchaServiceImpl implements CaptchaService {
|
|||||||
return validate(key, code, delete);
|
return validate(key, code, delete);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 生成指定数量的随机数验证码
|
|
||||||
*
|
|
||||||
* @param length 数量
|
|
||||||
* @return 随机码
|
|
||||||
*/
|
|
||||||
private String generateValidateCode(Integer length) {
|
|
||||||
String chars = "0123456789"; // 字符范围可以自定义:数字
|
|
||||||
Random random = new Random();
|
|
||||||
StringBuilder code = new StringBuilder();
|
|
||||||
for (int i = 0; i < length; i++) {
|
|
||||||
code.append(chars.charAt(random.nextInt(chars.length())));
|
|
||||||
}
|
|
||||||
return code.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setCache(String key, String value) {
|
private void setCache(String key, String value) {
|
||||||
if (open) {
|
if (open) {
|
||||||
key = RedisKeys.getCaptchaKey(key);
|
key = RedisKeys.getCaptchaKey(key);
|
||||||
@@ -180,4 +164,4 @@ public class CaptchaServiceImpl implements CaptchaService {
|
|||||||
}
|
}
|
||||||
return captcha;
|
return captcha;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -12,6 +12,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import xiaozhi.common.exception.RenException;
|
import xiaozhi.common.exception.RenException;
|
||||||
import xiaozhi.common.exception.ErrorCode;
|
import xiaozhi.common.exception.ErrorCode;
|
||||||
@@ -21,7 +22,6 @@ import xiaozhi.common.redis.RedisUtils;
|
|||||||
import xiaozhi.common.service.impl.BaseServiceImpl;
|
import xiaozhi.common.service.impl.BaseServiceImpl;
|
||||||
import xiaozhi.common.utils.ConvertUtils;
|
import xiaozhi.common.utils.ConvertUtils;
|
||||||
import xiaozhi.common.utils.JsonUtils;
|
import xiaozhi.common.utils.JsonUtils;
|
||||||
import xiaozhi.common.utils.ToolUtil;
|
|
||||||
import xiaozhi.modules.sys.dao.SysDictDataDao;
|
import xiaozhi.modules.sys.dao.SysDictDataDao;
|
||||||
import xiaozhi.modules.sys.dao.SysUserDao;
|
import xiaozhi.modules.sys.dao.SysUserDao;
|
||||||
import xiaozhi.modules.sys.dto.SysDictDataDTO;
|
import xiaozhi.modules.sys.dto.SysDictDataDTO;
|
||||||
@@ -104,13 +104,13 @@ public class SysDictDataServiceImpl extends BaseServiceImpl<SysDictDataDao, SysD
|
|||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void delete(Long[] ids) {
|
public void delete(Long[] ids) {
|
||||||
List<Long> idList = Arrays.asList(ids);
|
List<Long> idList = Arrays.asList(ids);
|
||||||
if (ToolUtil.isNotEmpty(idList)) {
|
if (CollUtil.isNotEmpty(idList)) {
|
||||||
//批量删除redis字典
|
//批量删除redis字典
|
||||||
List<String> redisKeyList = new ArrayList<>();
|
List<String> redisKeyList = new ArrayList<>();
|
||||||
//批量获取字典类型
|
//批量获取字典类型
|
||||||
List<String> dictTypeList = Optional.ofNullable(baseDao.getDictTypesByIdList(idList)).orElseGet(ArrayList::new);
|
List<String> dictTypeList = Optional.ofNullable(baseDao.getDictTypesByIdList(idList)).orElseGet(ArrayList::new);
|
||||||
dictTypeList.forEach(dictType -> redisKeyList.add(RedisKeys.getDictDataByTypeKey(dictType)));
|
dictTypeList.forEach(dictType -> redisKeyList.add(RedisKeys.getDictDataByTypeKey(dictType)));
|
||||||
if (ToolUtil.isNotEmpty(redisKeyList)) {
|
if (CollUtil.isNotEmpty(redisKeyList)) {
|
||||||
//清除缓存
|
//清除缓存
|
||||||
redisUtils.delete(redisKeyList);
|
redisUtils.delete(redisKeyList);
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -17,6 +17,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
|
|||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import xiaozhi.common.constant.Constant;
|
import xiaozhi.common.constant.Constant;
|
||||||
@@ -26,7 +27,6 @@ import xiaozhi.common.page.PageData;
|
|||||||
import xiaozhi.common.service.impl.BaseServiceImpl;
|
import xiaozhi.common.service.impl.BaseServiceImpl;
|
||||||
import xiaozhi.common.utils.ConvertUtils;
|
import xiaozhi.common.utils.ConvertUtils;
|
||||||
import xiaozhi.common.utils.DateUtils;
|
import xiaozhi.common.utils.DateUtils;
|
||||||
import xiaozhi.common.utils.ToolUtil;
|
|
||||||
import xiaozhi.modules.model.entity.ModelConfigEntity;
|
import xiaozhi.modules.model.entity.ModelConfigEntity;
|
||||||
import xiaozhi.modules.model.service.ModelConfigService;
|
import xiaozhi.modules.model.service.ModelConfigService;
|
||||||
import xiaozhi.modules.sys.dao.SysUserDao;
|
import xiaozhi.modules.sys.dao.SysUserDao;
|
||||||
@@ -120,7 +120,7 @@ public class VoiceCloneServiceImpl extends BaseServiceImpl<VoiceCloneDao, VoiceC
|
|||||||
entity.setTrainStatus(0); // 默认训练中
|
entity.setTrainStatus(0); // 默认训练中
|
||||||
batchInsertList.add(entity);
|
batchInsertList.add(entity);
|
||||||
}
|
}
|
||||||
if (ToolUtil.isNotEmpty(batchInsertList)) {
|
if (CollUtil.isNotEmpty(batchInsertList)) {
|
||||||
insertBatch(batchInsertList);
|
insertBatch(batchInsertList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+78
@@ -0,0 +1,78 @@
|
|||||||
|
package xiaozhi.modules.device.controller;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.mockStatic;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.MockedStatic;
|
||||||
|
|
||||||
|
import xiaozhi.common.exception.ErrorCode;
|
||||||
|
import xiaozhi.common.redis.RedisUtils;
|
||||||
|
import xiaozhi.common.user.UserDetail;
|
||||||
|
import xiaozhi.common.utils.MessageUtils;
|
||||||
|
import xiaozhi.common.utils.Result;
|
||||||
|
import xiaozhi.modules.device.dto.DeviceUpdateDTO;
|
||||||
|
import xiaozhi.modules.device.entity.DeviceEntity;
|
||||||
|
import xiaozhi.modules.device.service.DeviceAddressBookService;
|
||||||
|
import xiaozhi.modules.device.service.DeviceService;
|
||||||
|
import xiaozhi.modules.security.user.SecurityUser;
|
||||||
|
import xiaozhi.modules.sys.service.SysParamsService;
|
||||||
|
|
||||||
|
@DisplayName("设备更新接口回归测试")
|
||||||
|
class DeviceControllerTest {
|
||||||
|
|
||||||
|
private static final String DEVICE_ID = "device-id";
|
||||||
|
private static final long USER_ID = 1L;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("数据库未更新时不误报自动升级状态修改成功")
|
||||||
|
void updateFailureIsReturnedToCaller() {
|
||||||
|
DeviceService deviceService = mock(DeviceService.class);
|
||||||
|
DeviceEntity entity = ownedDevice();
|
||||||
|
when(deviceService.selectById(DEVICE_ID)).thenReturn(entity);
|
||||||
|
when(deviceService.updateById(entity)).thenReturn(false);
|
||||||
|
DeviceController controller = controller(deviceService);
|
||||||
|
|
||||||
|
DeviceUpdateDTO update = new DeviceUpdateDTO();
|
||||||
|
update.setAutoUpdate(0);
|
||||||
|
|
||||||
|
try (MockedStatic<SecurityUser> securityUser = mockStatic(SecurityUser.class);
|
||||||
|
MockedStatic<MessageUtils> messageUtils = mockStatic(MessageUtils.class)) {
|
||||||
|
securityUser.when(SecurityUser::getUser).thenReturn(currentUser());
|
||||||
|
messageUtils.when(() -> MessageUtils.getMessage(ErrorCode.UPDATE_DATA_FAILED))
|
||||||
|
.thenReturn("Failed to update data");
|
||||||
|
|
||||||
|
Result<Void> result = controller.updateDeviceInfo(DEVICE_ID, update);
|
||||||
|
|
||||||
|
assertEquals(ErrorCode.UPDATE_DATA_FAILED, result.getCode());
|
||||||
|
assertEquals(0, entity.getAutoUpdate());
|
||||||
|
verify(deviceService).updateById(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private DeviceController controller(DeviceService deviceService) {
|
||||||
|
return new DeviceController(
|
||||||
|
deviceService,
|
||||||
|
mock(DeviceAddressBookService.class),
|
||||||
|
mock(RedisUtils.class),
|
||||||
|
mock(SysParamsService.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
private DeviceEntity ownedDevice() {
|
||||||
|
DeviceEntity entity = new DeviceEntity();
|
||||||
|
entity.setId(DEVICE_ID);
|
||||||
|
entity.setUserId(USER_ID);
|
||||||
|
entity.setAutoUpdate(1);
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
private UserDetail currentUser() {
|
||||||
|
UserDetail user = new UserDetail();
|
||||||
|
user.setId(USER_ID);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
+104
@@ -0,0 +1,104 @@
|
|||||||
|
package xiaozhi.modules.device.service.impl;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.verifyNoInteractions;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.aop.framework.ProxyFactory;
|
||||||
|
|
||||||
|
import xiaozhi.common.constant.Constant;
|
||||||
|
import xiaozhi.modules.device.dto.DeviceReportReqDTO;
|
||||||
|
import xiaozhi.modules.device.dto.DeviceReportRespDTO;
|
||||||
|
import xiaozhi.modules.device.entity.DeviceEntity;
|
||||||
|
import xiaozhi.modules.device.service.OtaService;
|
||||||
|
import xiaozhi.modules.sys.service.SysParamsService;
|
||||||
|
|
||||||
|
@DisplayName("设备自动升级回归测试")
|
||||||
|
class DeviceAutoUpdateTest {
|
||||||
|
|
||||||
|
private static final String MAC_ADDRESS = "00:11:22:33:44:55";
|
||||||
|
private static final String BOARD_TYPE = "test-board";
|
||||||
|
private static final String CURRENT_VERSION = "1.0.0";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("#3299 自动升级关闭时 OTA 响应不包含固件")
|
||||||
|
void disabledAutoUpdateOmitsFirmware() {
|
||||||
|
OtaService otaService = mock(OtaService.class);
|
||||||
|
DeviceServiceImpl service = proxiedService(deviceWithAutoUpdate(0), otaService);
|
||||||
|
|
||||||
|
DeviceReportRespDTO response = service.checkDeviceActive(
|
||||||
|
MAC_ADDRESS, MAC_ADDRESS, deviceReport());
|
||||||
|
|
||||||
|
assertNull(response.getFirmware());
|
||||||
|
verifyNoInteractions(otaService);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("自动升级开启时继续执行固件查询")
|
||||||
|
void enabledAutoUpdateChecksFirmware() {
|
||||||
|
OtaService otaService = mock(OtaService.class);
|
||||||
|
when(otaService.getLatestOta(BOARD_TYPE)).thenReturn(null);
|
||||||
|
DeviceServiceImpl service = proxiedService(deviceWithAutoUpdate(1), otaService);
|
||||||
|
|
||||||
|
DeviceReportRespDTO response = service.checkDeviceActive(
|
||||||
|
MAC_ADDRESS, MAC_ADDRESS, deviceReport());
|
||||||
|
|
||||||
|
assertNotNull(response.getFirmware());
|
||||||
|
assertEquals(CURRENT_VERSION, response.getFirmware().getVersion());
|
||||||
|
assertEquals(Constant.INVALID_FIRMWARE_URL, response.getFirmware().getUrl());
|
||||||
|
verify(otaService).getLatestOta(BOARD_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DeviceServiceImpl proxiedService(DeviceEntity device, OtaService otaService) {
|
||||||
|
SysParamsService sysParamsService = mock(SysParamsService.class);
|
||||||
|
when(sysParamsService.getValue(Constant.SERVER_WEBSOCKET, true))
|
||||||
|
.thenReturn("ws://127.0.0.1:8000/xiaozhi/v1/");
|
||||||
|
when(sysParamsService.getValue(Constant.SERVER_AUTH_ENABLED, true)).thenReturn("false");
|
||||||
|
when(sysParamsService.getValue(Constant.SERVER_MQTT_GATEWAY, true)).thenReturn(null);
|
||||||
|
|
||||||
|
DeviceServiceImpl target = new DeviceServiceImpl(
|
||||||
|
null, null, sysParamsService, null, otaService, null) {
|
||||||
|
@Override
|
||||||
|
public DeviceEntity getDeviceByMacAddress(String macAddress) {
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateDeviceConnectionInfo(String agentId, String deviceId, String appVersion) {
|
||||||
|
// No-op: connection timestamps are outside this OTA decision test.
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ProxyFactory proxyFactory = new ProxyFactory(target);
|
||||||
|
proxyFactory.setProxyTargetClass(true);
|
||||||
|
proxyFactory.setExposeProxy(true);
|
||||||
|
return (DeviceServiceImpl) proxyFactory.getProxy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private DeviceEntity deviceWithAutoUpdate(int autoUpdate) {
|
||||||
|
DeviceEntity device = new DeviceEntity();
|
||||||
|
device.setId(MAC_ADDRESS);
|
||||||
|
device.setMacAddress(MAC_ADDRESS);
|
||||||
|
device.setBoard(BOARD_TYPE);
|
||||||
|
device.setAutoUpdate(autoUpdate);
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DeviceReportReqDTO deviceReport() {
|
||||||
|
DeviceReportReqDTO.Application application = new DeviceReportReqDTO.Application();
|
||||||
|
application.setVersion(CURRENT_VERSION);
|
||||||
|
DeviceReportReqDTO.BoardInfo board = new DeviceReportReqDTO.BoardInfo();
|
||||||
|
board.setType(BOARD_TYPE);
|
||||||
|
|
||||||
|
DeviceReportReqDTO report = new DeviceReportReqDTO();
|
||||||
|
report.setApplication(application);
|
||||||
|
report.setBoard(board);
|
||||||
|
return report;
|
||||||
|
}
|
||||||
|
}
|
||||||
+19
@@ -76,6 +76,25 @@ class DeviceTimeSerializationTest {
|
|||||||
() -> assertTrue(payload.path("createDate").isNull()));
|
() -> assertTrue(payload.path("createDate").isNull()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest(name = "自动升级状态 {0}")
|
||||||
|
@ValueSource(ints = { 0, 1 })
|
||||||
|
@DisplayName("#3299 设备列表按 autoUpdate 契约返回真实开关状态")
|
||||||
|
void serializedDeviceContainsAutoUpdateState(int autoUpdate) {
|
||||||
|
DeviceEntity entity = new DeviceEntity();
|
||||||
|
entity.setAutoUpdate(autoUpdate);
|
||||||
|
DeviceServiceImpl deviceService = serviceReturning(entity);
|
||||||
|
|
||||||
|
UserShowDeviceListVO device = deviceService.getUserDeviceList(1L, "agent-id").getFirst();
|
||||||
|
ObjectMapper objectMapper = new WebMvcConfig().jackson2HttpMessageConverter().getObjectMapper();
|
||||||
|
JsonNode payload = objectMapper.valueToTree(device);
|
||||||
|
|
||||||
|
assertAll(
|
||||||
|
() -> assertEquals(autoUpdate, device.getAutoUpdate()),
|
||||||
|
() -> assertEquals(autoUpdate, payload.path("autoUpdate").asInt()),
|
||||||
|
() -> assertTrue(payload.path("otaUpgrade").isMissingNode(),
|
||||||
|
"设备列表不应继续暴露未映射的旧字段 otaUpgrade"));
|
||||||
|
}
|
||||||
|
|
||||||
private DeviceServiceImpl serviceReturning(DeviceEntity entity) {
|
private DeviceServiceImpl serviceReturning(DeviceEntity entity) {
|
||||||
return new DeviceServiceImpl(null, null, null, null, null, null) {
|
return new DeviceServiceImpl(null, null, null, null, null, null) {
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Reference in New Issue
Block a user