mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-23 15:43:54 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
942d55118b | ||
|
|
f47f3b050b | ||
|
|
e3453b1a87 | ||
|
|
d760465408 | ||
|
|
bdb2538239 | ||
|
|
ea6c144e43 | ||
|
|
a79aa455d6 | ||
|
|
6e92d169ec | ||
|
|
ed89c05245 | ||
|
|
b500d1c6bd | ||
|
|
0598b09629 | ||
|
|
e4907121f4 | ||
|
|
2a618d2f8f |
@@ -33,6 +33,7 @@
|
|||||||
<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>
|
||||||
<skipTests>true</skipTests>
|
<skipTests>true</skipTests>
|
||||||
|
<argLine></argLine>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@@ -272,11 +273,22 @@
|
|||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<proc>full</proc>
|
||||||
|
<compilerArgs>
|
||||||
|
<arg>-Xlint:deprecation,unchecked</arg>
|
||||||
|
</compilerArgs>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
<configuration>
|
<configuration>
|
||||||
<skipTests>${skipTests}</skipTests>
|
<skipTests>${skipTests}</skipTests>
|
||||||
|
<argLine>@{argLine} -Xshare:off -javaagent:"${settings.localRepository}/org/mockito/mockito-core/${mockito.version}/mockito-core-${mockito.version}.jar"</argLine>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
|
|||||||
+2
-2
@@ -67,7 +67,7 @@ public class DataFilterInterceptor implements InnerInterceptor {
|
|||||||
private String getSelect(String buildSql, DataScope scope) {
|
private String getSelect(String buildSql, DataScope scope) {
|
||||||
try {
|
try {
|
||||||
Select select = (Select) CCJSqlParserUtil.parse(buildSql);
|
Select select = (Select) CCJSqlParserUtil.parse(buildSql);
|
||||||
PlainSelect plainSelect = (PlainSelect) select.getSelectBody();
|
PlainSelect plainSelect = select.getPlainSelect();
|
||||||
|
|
||||||
Expression expression = plainSelect.getWhere();
|
Expression expression = plainSelect.getWhere();
|
||||||
if (expression == null) {
|
if (expression == null) {
|
||||||
@@ -82,4 +82,4 @@ public class DataFilterInterceptor implements InnerInterceptor {
|
|||||||
return buildSql;
|
return buildSql;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,8 +81,8 @@ public abstract class BaseServiceImpl<M extends BaseMapper<T>, T> implements Bas
|
|||||||
// 处理排序字段
|
// 处理排序字段
|
||||||
if (orderField instanceof String) {
|
if (orderField instanceof String) {
|
||||||
orderFields.add((String) orderField);
|
orderFields.add((String) orderField);
|
||||||
} else if (orderField instanceof List) {
|
} else if (orderField instanceof List<?> fields) {
|
||||||
orderFields.addAll((List<String>) orderField);
|
fields.forEach(field -> orderFields.add(String.class.cast(field)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 有排序字段则排序
|
// 有排序字段则排序
|
||||||
@@ -142,11 +142,12 @@ public abstract class BaseServiceImpl<M extends BaseMapper<T>, T> implements Bas
|
|||||||
return SqlHelper.retBool(result);
|
return SqlHelper.retBool(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Class<M> currentMapperClass() {
|
protected Class<?> currentMapperClass() {
|
||||||
return (Class<M>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseServiceImpl.class, 0);
|
return ReflectionKit.getSuperClassGenericType(this.getClass(), BaseServiceImpl.class, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public Class<T> currentModelClass() {
|
public Class<T> currentModelClass() {
|
||||||
return (Class<T>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseServiceImpl.class, 1);
|
return (Class<T>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseServiceImpl.class, 1);
|
||||||
}
|
}
|
||||||
@@ -226,6 +227,6 @@ public abstract class BaseServiceImpl<M extends BaseMapper<T>, T> implements Bas
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean deleteBatchIds(Collection<? extends Serializable> idList) {
|
public boolean deleteBatchIds(Collection<? extends Serializable> idList) {
|
||||||
return SqlHelper.retBool(baseDao.deleteBatchIds(idList));
|
return SqlHelper.retBool(baseDao.deleteByIds(idList));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import xiaozhi.common.utils.ConvertUtils;
|
|||||||
public abstract class CrudServiceImpl<M extends BaseMapper<T>, T, D> extends BaseServiceImpl<M, T>
|
public abstract class CrudServiceImpl<M extends BaseMapper<T>, T, D> extends BaseServiceImpl<M, T>
|
||||||
implements CrudService<T, D> {
|
implements CrudService<T, D> {
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
protected Class<D> currentDtoClass() {
|
protected Class<D> currentDtoClass() {
|
||||||
return (Class<D>) ReflectionKit.getSuperClassGenericType(getClass(), CrudServiceImpl.class, 2);
|
return (Class<D>) ReflectionKit.getSuperClassGenericType(getClass(), CrudServiceImpl.class, 2);
|
||||||
}
|
}
|
||||||
@@ -70,6 +71,6 @@ public abstract class CrudServiceImpl<M extends BaseMapper<T>, T, D> extends Bas
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void delete(Serializable[] ids) {
|
public void delete(Serializable[] ids) {
|
||||||
baseDao.deleteBatchIds(Arrays.asList(ids));
|
baseDao.deleteByIds(Arrays.asList(ids));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package xiaozhi.common.utils;
|
package xiaozhi.common.utils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
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;
|
||||||
@@ -16,6 +18,10 @@ import cn.hutool.core.util.StrUtil;
|
|||||||
*/
|
*/
|
||||||
public class JsonUtils {
|
public class JsonUtils {
|
||||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
private static final TypeReference<Map<String, Object>> STRING_OBJECT_MAP = new TypeReference<>() {
|
||||||
|
};
|
||||||
|
private static final TypeReference<List<Map<String, Object>>> STRING_OBJECT_MAP_LIST = new TypeReference<>() {
|
||||||
|
};
|
||||||
|
|
||||||
public static String toJsonString(Object object) {
|
public static String toJsonString(Object object) {
|
||||||
try {
|
try {
|
||||||
@@ -67,4 +73,59 @@ public class JsonUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Map<String, Object> parseMap(String text) {
|
||||||
|
if (StrUtil.isEmpty(text)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return parseObject(text, STRING_OBJECT_MAP);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Map<String, Object>> parseMapList(String text) {
|
||||||
|
if (StrUtil.isEmpty(text)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return parseObject(text, STRING_OBJECT_MAP_LIST);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, Object> toStringObjectMap(Object value) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!(value instanceof Map<?, ?> map)) {
|
||||||
|
throw new ClassCastException("Expected Map but got " + value.getClass().getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> result = new LinkedHashMap<>(map.size());
|
||||||
|
for (Map.Entry<?, ?> entry : map.entrySet()) {
|
||||||
|
result.put(String.class.cast(entry.getKey()), entry.getValue());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Map<String, Object>> toStringObjectMapList(Object value) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<?> list = List.class.cast(value);
|
||||||
|
List<Map<String, Object>> result = new ArrayList<>(list.size());
|
||||||
|
for (Object item : list) {
|
||||||
|
result.add(toStringObjectMap(item));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> List<T> toList(Object value, Class<T> elementType) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<?> list = List.class.cast(value);
|
||||||
|
List<T> result = new ArrayList<>(list.size());
|
||||||
|
for (Object item : list) {
|
||||||
|
result.add(elementType.cast(item));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,11 +75,11 @@ public class SensitiveDataUtils {
|
|||||||
Object value = jsonObject.get(key);
|
Object value = jsonObject.get(key);
|
||||||
|
|
||||||
if (SENSITIVE_FIELDS.contains(key.toLowerCase()) && value instanceof String) {
|
if (SENSITIVE_FIELDS.contains(key.toLowerCase()) && value instanceof String) {
|
||||||
result.put(key, maskMiddle((String) value));
|
result.set(key, maskMiddle((String) value));
|
||||||
} else if (value instanceof JSONObject) {
|
} else if (value instanceof JSONObject) {
|
||||||
result.put(key, maskSensitiveFields((JSONObject) value));
|
result.set(key, maskSensitiveFields((JSONObject) value));
|
||||||
} else {
|
} else {
|
||||||
result.put(key, value);
|
result.set(key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,4 +162,4 @@ public class SensitiveDataUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,10 +22,10 @@ public class SqlFilter {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// 去掉'|"|;|\字符
|
// 去掉'|"|;|\字符
|
||||||
str = StringUtils.replace(str, "'", "");
|
str = str.replace("'", "");
|
||||||
str = StringUtils.replace(str, "\"", "");
|
str = str.replace("\"", "");
|
||||||
str = StringUtils.replace(str, ";", "");
|
str = str.replace(";", "");
|
||||||
str = StringUtils.replace(str, "\\", "");
|
str = str.replace("\\", "");
|
||||||
|
|
||||||
// 转换成小写
|
// 转换成小写
|
||||||
str = str.toLowerCase();
|
str = str.toLowerCase();
|
||||||
|
|||||||
+3
-2
@@ -34,6 +34,7 @@ import xiaozhi.common.page.PageData;
|
|||||||
import xiaozhi.common.redis.RedisKeys;
|
import xiaozhi.common.redis.RedisKeys;
|
||||||
import xiaozhi.common.redis.RedisUtils;
|
import xiaozhi.common.redis.RedisUtils;
|
||||||
import xiaozhi.common.user.UserDetail;
|
import xiaozhi.common.user.UserDetail;
|
||||||
|
import xiaozhi.common.utils.JsonUtils;
|
||||||
import xiaozhi.common.utils.Result;
|
import xiaozhi.common.utils.Result;
|
||||||
import xiaozhi.common.utils.ResultUtils;
|
import xiaozhi.common.utils.ResultUtils;
|
||||||
import xiaozhi.modules.agent.dto.AgentChatHistoryDTO;
|
import xiaozhi.modules.agent.dto.AgentChatHistoryDTO;
|
||||||
@@ -340,8 +341,8 @@ public class AgentController {
|
|||||||
@RequiresPermissions("sys:role:normal")
|
@RequiresPermissions("sys:role:normal")
|
||||||
public Result<Void> saveAgentTags(@PathVariable String id, @RequestBody Map<String, Object> params) {
|
public Result<Void> saveAgentTags(@PathVariable String id, @RequestBody Map<String, Object> params) {
|
||||||
requireAgentPermission(id);
|
requireAgentPermission(id);
|
||||||
List<String> tagIds = (List<String>) params.get("tagIds");
|
List<String> tagIds = JsonUtils.toList(params.get("tagIds"), String.class);
|
||||||
List<String> tagNames = (List<String>) params.get("tagNames");
|
List<String> tagNames = JsonUtils.toList(params.get("tagNames"), String.class);
|
||||||
AgentUpdateDTO dto = new AgentUpdateDTO();
|
AgentUpdateDTO dto = new AgentUpdateDTO();
|
||||||
dto.setTagIds(tagIds);
|
dto.setTagIds(tagIds);
|
||||||
dto.setTagNames(tagNames);
|
dto.setTagNames(tagNames);
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ public class AgentDTO {
|
|||||||
private String systemPrompt;
|
private String systemPrompt;
|
||||||
|
|
||||||
@Schema(description = "总结记忆", example = "构建可生长的动态记忆网络,在有限空间内保留关键信息的同时,智能维护信息演变轨迹\n" +
|
@Schema(description = "总结记忆", example = "构建可生长的动态记忆网络,在有限空间内保留关键信息的同时,智能维护信息演变轨迹\n" +
|
||||||
"根据对话记录,总结user的重要信息,以便在未来的对话中提供更个性化的服务", required = false)
|
"根据对话记录,总结user的重要信息,以便在未来的对话中提供更个性化的服务", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
|
||||||
private String summaryMemory;
|
private String summaryMemory;
|
||||||
|
|
||||||
@Schema(description = "最后连接时间", example = "2024-03-20 10:00:00")
|
@Schema(description = "最后连接时间", example = "2024-03-20 10:00:00")
|
||||||
@@ -50,4 +50,4 @@ public class AgentDTO {
|
|||||||
|
|
||||||
@Schema(description = "标签列表")
|
@Schema(description = "标签列表")
|
||||||
private List<AgentTagDTO> tags;
|
private List<AgentTagDTO> tags;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,6 @@ public class AgentMemoryDTO implements Serializable {
|
|||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@Schema(description = "总结记忆", example = "构建可生长的动态记忆网络,在有限空间内保留关键信息的同时,智能维护信息演变轨迹\n" +
|
@Schema(description = "总结记忆", example = "构建可生长的动态记忆网络,在有限空间内保留关键信息的同时,智能维护信息演变轨迹\n" +
|
||||||
"根据对话记录,总结user的重要信息,以便在未来的对话中提供更个性化的服务", required = false)
|
"根据对话记录,总结user的重要信息,以便在未来的对话中提供更个性化的服务", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
|
||||||
private String summaryMemory;
|
private String summaryMemory;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,10 +39,10 @@ public class AgentUpdateDTO implements Serializable {
|
|||||||
@Schema(description = "小模型标识", example = "slm_model_02", nullable = true)
|
@Schema(description = "小模型标识", example = "slm_model_02", nullable = true)
|
||||||
private String slmModelId;
|
private String slmModelId;
|
||||||
|
|
||||||
@Schema(description = "VLLM模型标识", example = "vllm_model_02", required = false)
|
@Schema(description = "VLLM模型标识", example = "vllm_model_02", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
|
||||||
private String vllmModelId;
|
private String vllmModelId;
|
||||||
|
|
||||||
@Schema(description = "语音合成模型标识", example = "tts_model_02", required = false)
|
@Schema(description = "语音合成模型标识", example = "tts_model_02", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
|
||||||
private String ttsModelId;
|
private String ttsModelId;
|
||||||
|
|
||||||
@Schema(description = "音色标识", example = "voice_02", nullable = true)
|
@Schema(description = "音色标识", example = "voice_02", nullable = true)
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ public class AgentEntity {
|
|||||||
private String systemPrompt;
|
private String systemPrompt;
|
||||||
|
|
||||||
@Schema(description = "总结记忆", example = "构建可生长的动态记忆网络,在有限空间内保留关键信息的同时,智能维护信息演变轨迹\n" +
|
@Schema(description = "总结记忆", example = "构建可生长的动态记忆网络,在有限空间内保留关键信息的同时,智能维护信息演变轨迹\n" +
|
||||||
"根据对话记录,总结user的重要信息,以便在未来的对话中提供更个性化的服务", required = false)
|
"根据对话记录,总结user的重要信息,以便在未来的对话中提供更个性化的服务", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
|
||||||
private String summaryMemory;
|
private String summaryMemory;
|
||||||
|
|
||||||
@Schema(description = "语言编码")
|
@Schema(description = "语言编码")
|
||||||
@@ -97,4 +97,4 @@ public class AgentEntity {
|
|||||||
|
|
||||||
@Schema(description = "更新时间")
|
@Schema(description = "更新时间")
|
||||||
private Date updatedAt;
|
private Date updatedAt;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -167,7 +167,7 @@ public class AgentChatHistoryServiceImpl extends CrudRepository<AiAgentChatHisto
|
|||||||
|
|
||||||
// 尝试解析为 JSON
|
// 尝试解析为 JSON
|
||||||
try {
|
try {
|
||||||
Map<String, Object> jsonMap = JsonUtils.parseObject(content, Map.class);
|
Map<String, Object> jsonMap = JsonUtils.parseMap(content);
|
||||||
if (jsonMap != null && jsonMap.containsKey("content")) {
|
if (jsonMap != null && jsonMap.containsKey("content")) {
|
||||||
Object contentObj = jsonMap.get("content");
|
Object contentObj = jsonMap.get("content");
|
||||||
return contentObj != null ? contentObj.toString() : content;
|
return contentObj != null ? contentObj.toString() : content;
|
||||||
|
|||||||
+10
-10
@@ -76,7 +76,7 @@ public class AgentMcpAccessPointServiceImpl implements AgentMcpAccessPointServic
|
|||||||
// 等待初始化响应 (id=1) - 移除固定延迟,改为响应驱动
|
// 等待初始化响应 (id=1) - 移除固定延迟,改为响应驱动
|
||||||
List<String> initResponses = client.listenerWithoutClose(response -> {
|
List<String> initResponses = client.listenerWithoutClose(response -> {
|
||||||
try {
|
try {
|
||||||
Map<String, Object> jsonMap = JsonUtils.parseObject(response, Map.class);
|
Map<String, Object> jsonMap = JsonUtils.parseMap(response);
|
||||||
if (jsonMap != null && Integer.valueOf(1).equals(jsonMap.get("id"))) {
|
if (jsonMap != null && Integer.valueOf(1).equals(jsonMap.get("id"))) {
|
||||||
// 检查是否有result字段,表示初始化成功
|
// 检查是否有result字段,表示初始化成功
|
||||||
return jsonMap.containsKey("result") && !jsonMap.containsKey("error");
|
return jsonMap.containsKey("result") && !jsonMap.containsKey("error");
|
||||||
@@ -92,7 +92,7 @@ public class AgentMcpAccessPointServiceImpl implements AgentMcpAccessPointServic
|
|||||||
boolean initSucceeded = false;
|
boolean initSucceeded = false;
|
||||||
for (String response : initResponses) {
|
for (String response : initResponses) {
|
||||||
try {
|
try {
|
||||||
Map<String, Object> jsonMap = JsonUtils.parseObject(response, Map.class);
|
Map<String, Object> jsonMap = JsonUtils.parseMap(response);
|
||||||
if (jsonMap != null && Integer.valueOf(1).equals(jsonMap.get("id"))) {
|
if (jsonMap != null && Integer.valueOf(1).equals(jsonMap.get("id"))) {
|
||||||
if (jsonMap.containsKey("result")) {
|
if (jsonMap.containsKey("result")) {
|
||||||
log.info("MCP初始化成功,智能体ID: {}", id);
|
log.info("MCP初始化成功,智能体ID: {}", id);
|
||||||
@@ -123,7 +123,7 @@ public class AgentMcpAccessPointServiceImpl implements AgentMcpAccessPointServic
|
|||||||
// 等待工具列表响应 (id=2)
|
// 等待工具列表响应 (id=2)
|
||||||
List<String> toolsResponses = client.listener(response -> {
|
List<String> toolsResponses = client.listener(response -> {
|
||||||
try {
|
try {
|
||||||
Map<String, Object> jsonMap = JsonUtils.parseObject(response, Map.class);
|
Map<String, Object> jsonMap = JsonUtils.parseMap(response);
|
||||||
return jsonMap != null && Integer.valueOf(2).equals(jsonMap.get("id"));
|
return jsonMap != null && Integer.valueOf(2).equals(jsonMap.get("id"));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("解析工具列表响应失败: {}", response, e);
|
log.warn("解析工具列表响应失败: {}", response, e);
|
||||||
@@ -134,18 +134,18 @@ public class AgentMcpAccessPointServiceImpl implements AgentMcpAccessPointServic
|
|||||||
// 处理工具列表响应
|
// 处理工具列表响应
|
||||||
for (String response : toolsResponses) {
|
for (String response : toolsResponses) {
|
||||||
try {
|
try {
|
||||||
Map<String, Object> jsonMap = JsonUtils.parseObject(response, Map.class);
|
Map<String, Object> jsonMap = JsonUtils.parseMap(response);
|
||||||
if (jsonMap != null && Integer.valueOf(2).equals(jsonMap.get("id"))) {
|
if (jsonMap != null && Integer.valueOf(2).equals(jsonMap.get("id"))) {
|
||||||
// 检查是否有result字段
|
// 检查是否有result字段
|
||||||
Object resultObj = jsonMap.get("result");
|
Object resultObj = jsonMap.get("result");
|
||||||
if (resultObj instanceof Map) {
|
if (resultObj instanceof Map<?, ?>) {
|
||||||
Map<String, Object> resultMap = (Map<String, Object>) resultObj;
|
Map<String, Object> resultMap = JsonUtils.toStringObjectMap(resultObj);
|
||||||
Object toolsObj = resultMap.get("tools");
|
Object toolsObj = resultMap.get("tools");
|
||||||
if (toolsObj instanceof List) {
|
if (toolsObj instanceof List<?>) {
|
||||||
List<Map<String, Object>> toolsList = (List<Map<String, Object>>) toolsObj;
|
List<Map<String, Object>> toolsList = JsonUtils.toStringObjectMapList(toolsObj);
|
||||||
// 提取工具名称列表
|
// 提取工具名称列表
|
||||||
List<String> result = toolsList.stream()
|
List<String> result = toolsList.stream()
|
||||||
.map(tool -> (String) tool.get("name"))
|
.map(tool -> String.class.cast(tool.get("name")))
|
||||||
.filter(name -> name != null)
|
.filter(name -> name != null)
|
||||||
.sorted()
|
.sorted()
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
@@ -232,4 +232,4 @@ public class AgentMcpAccessPointServiceImpl implements AgentMcpAccessPointServic
|
|||||||
// 加密后成token值
|
// 加密后成token值
|
||||||
return AESUtils.encrypt(key, json);
|
return AESUtils.encrypt(key, json);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -677,7 +677,7 @@ public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> imp
|
|||||||
mapping.setPluginId(pluginId);
|
mapping.setPluginId(pluginId);
|
||||||
|
|
||||||
Map<String, Object> paramInfo = new HashMap<>();
|
Map<String, Object> paramInfo = new HashMap<>();
|
||||||
List<Map<String, Object>> fields = JsonUtils.parseObject(provider.getFields(), List.class);
|
List<Map<String, Object>> fields = JsonUtils.parseMapList(provider.getFields());
|
||||||
if (fields != null) {
|
if (fields != null) {
|
||||||
for (Map<String, Object> field : fields) {
|
for (Map<String, Object> field : fields) {
|
||||||
paramInfo.put((String) field.get("key"), field.get("default"));
|
paramInfo.put((String) field.get("key"), field.get("default"));
|
||||||
|
|||||||
+1
-1
@@ -133,7 +133,7 @@ public class AgentTagServiceImpl extends BaseServiceImpl<AgentTagDao, AgentTagEn
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (tagIds != null && !tagIds.isEmpty()) {
|
if (tagIds != null && !tagIds.isEmpty()) {
|
||||||
List<AgentTagEntity> tagIdEntities = baseDao.selectBatchIds(tagIds);
|
List<AgentTagEntity> tagIdEntities = baseDao.selectByIds(tagIds);
|
||||||
for (AgentTagEntity tag : tagIdEntities) {
|
for (AgentTagEntity tag : tagIdEntities) {
|
||||||
if (tag != null && (currentTagNames.contains(tag.getTagName()) ||
|
if (tag != null && (currentTagNames.contains(tag.getTagName()) ||
|
||||||
newTagNames.contains(tag.getTagName()))) {
|
newTagNames.contains(tag.getTagName()))) {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ public interface ConfigService {
|
|||||||
* @param isCache 是否缓存
|
* @param isCache 是否缓存
|
||||||
* @return 配置信息
|
* @return 配置信息
|
||||||
*/
|
*/
|
||||||
Object getConfig(Boolean isCache);
|
Map<String, Object> getConfig(Boolean isCache);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取智能体模型配置
|
* 获取智能体模型配置
|
||||||
@@ -28,4 +28,4 @@ public interface ConfigService {
|
|||||||
* @return 替换词列表,格式如 ["模板1|模板01", "模板2|模板02"]
|
* @return 替换词列表,格式如 ["模板1|模板01", "模板2|模板02"]
|
||||||
*/
|
*/
|
||||||
List<String> getCorrectWords(String macAddress);
|
List<String> getCorrectWords(String macAddress);
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-7
@@ -65,12 +65,12 @@ public class ConfigServiceImpl implements ConfigService {
|
|||||||
private final CorrectWordFileService correctWordFileService;
|
private final CorrectWordFileService correctWordFileService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getConfig(Boolean isCache) {
|
public Map<String, Object> getConfig(Boolean isCache) {
|
||||||
if (isCache) {
|
if (isCache) {
|
||||||
// 先从Redis获取配置
|
// 先从Redis获取配置
|
||||||
Object cachedConfig = redisUtils.get(RedisKeys.getServerConfigKey());
|
Object cachedConfig = redisUtils.get(RedisKeys.getServerConfigKey());
|
||||||
if (cachedConfig != null) {
|
if (cachedConfig != null) {
|
||||||
return cachedConfig;
|
return JsonUtils.toStringObjectMap(cachedConfig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,7 +123,7 @@ public class ConfigServiceImpl implements ConfigService {
|
|||||||
if (isAdminRequest != null && "true".equals(isAdminRequest)) {
|
if (isAdminRequest != null && "true".equals(isAdminRequest)) {
|
||||||
// 管理控制台请求,返回getConfig的结果
|
// 管理控制台请求,返回getConfig的结果
|
||||||
redisUtils.delete(redisKey); // 使用后清理
|
redisUtils.delete(redisKey); // 使用后清理
|
||||||
return (Map<String, Object>) getConfig(true);
|
return getConfig(true);
|
||||||
}
|
}
|
||||||
// 根据MAC地址查找设备
|
// 根据MAC地址查找设备
|
||||||
DeviceEntity device = deviceService.getDeviceByMacAddress(macAddress);
|
DeviceEntity device = deviceService.getDeviceByMacAddress(macAddress);
|
||||||
@@ -277,10 +277,10 @@ public class ConfigServiceImpl implements ConfigService {
|
|||||||
// 遍历除最后一个key之外的所有key
|
// 遍历除最后一个key之外的所有key
|
||||||
for (int i = 0; i < keys.length - 1; i++) {
|
for (int i = 0; i < keys.length - 1; i++) {
|
||||||
String key = keys[i];
|
String key = keys[i];
|
||||||
if (!current.containsKey(key)) {
|
Object nestedConfig = current.computeIfAbsent(key, ignored -> new HashMap<String, Object>());
|
||||||
current.put(key, new HashMap<String, Object>());
|
Map<String, Object> nestedMap = JsonUtils.toStringObjectMap(nestedConfig);
|
||||||
}
|
current.put(key, nestedMap);
|
||||||
current = (Map<String, Object>) current.get(key);
|
current = nestedMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理最后一个key
|
// 处理最后一个key
|
||||||
|
|||||||
@@ -12,6 +12,11 @@ import xiaozhi.modules.device.entity.DeviceAddressBookEntity;
|
|||||||
@Mapper
|
@Mapper
|
||||||
public interface DeviceAddressBookDao extends BaseMapper<DeviceAddressBookEntity> {
|
public interface DeviceAddressBookDao extends BaseMapper<DeviceAddressBookEntity> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增设备通讯录记录
|
||||||
|
*/
|
||||||
|
int insertAddressBook(DeviceAddressBookEntity entity);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取设备通讯录列表
|
* 获取设备通讯录列表
|
||||||
*/
|
*/
|
||||||
@@ -31,4 +36,4 @@ public interface DeviceAddressBookDao extends BaseMapper<DeviceAddressBookEntity
|
|||||||
* 批量删除设备相关的通讯录记录
|
* 批量删除设备相关的通讯录记录
|
||||||
*/
|
*/
|
||||||
void deleteByMacAddresses(@Param("macAddresses") List<String> macAddresses);
|
void deleteByMacAddresses(@Param("macAddresses") List<String> macAddresses);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -167,7 +167,7 @@ public class DeviceAddressBookServiceImpl implements DeviceAddressBookService {
|
|||||||
alias = generateUniqueAlias(macAddress, targetMac, alias);
|
alias = generateUniqueAlias(macAddress, targetMac, alias);
|
||||||
entity.setAlias(alias);
|
entity.setAlias(alias);
|
||||||
entity.setHasPermission(hasPermission);
|
entity.setHasPermission(hasPermission);
|
||||||
deviceAddressBookDao.insert(entity);
|
deviceAddressBookDao.insertAddressBook(entity);
|
||||||
} else {
|
} else {
|
||||||
if (alias != null) {
|
if (alias != null) {
|
||||||
updateAlias(macAddress, targetMac, alias);
|
updateAlias(macAddress, targetMac, alias);
|
||||||
|
|||||||
+3
-2
@@ -50,6 +50,7 @@ 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.DateUtils;
|
import xiaozhi.common.utils.DateUtils;
|
||||||
|
import xiaozhi.common.utils.JsonUtils;
|
||||||
import xiaozhi.common.utils.ToolUtil;
|
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;
|
||||||
@@ -109,7 +110,7 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
|
|||||||
String deviceId = (String) cacheDeviceId;
|
String deviceId = (String) 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 = (Map<String, Object>) redisUtils.get(cacheDeviceKey);
|
Map<String, Object> cacheMap = JsonUtils.toStringObjectMap(redisUtils.get(cacheDeviceKey));
|
||||||
if (ToolUtil.isEmpty(cacheMap)) {
|
if (ToolUtil.isEmpty(cacheMap)) {
|
||||||
throw new RenException(ErrorCode.ACTIVATION_CODE_ERROR);
|
throw new RenException(ErrorCode.ACTIVATION_CODE_ERROR);
|
||||||
}
|
}
|
||||||
@@ -411,7 +412,7 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
|
|||||||
public String geCodeByDeviceId(String deviceId) {
|
public String geCodeByDeviceId(String deviceId) {
|
||||||
String dataKey = getDeviceCacheKey(deviceId);
|
String dataKey = getDeviceCacheKey(deviceId);
|
||||||
|
|
||||||
Map<String, Object> cacheMap = (Map<String, Object>) redisUtils.get(dataKey);
|
Map<String, Object> cacheMap = JsonUtils.toStringObjectMap(redisUtils.get(dataKey));
|
||||||
if (cacheMap != null && cacheMap.containsKey("activation_code")) {
|
if (cacheMap != null && cacheMap.containsKey("activation_code")) {
|
||||||
String cachedCode = (String) cacheMap.get("activation_code");
|
String cachedCode = (String) cacheMap.get("activation_code");
|
||||||
return cachedCode;
|
return cachedCode;
|
||||||
|
|||||||
+2
-2
@@ -56,7 +56,7 @@ public class OtaServiceImpl extends BaseServiceImpl<OtaDao, OtaEntity> implement
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void delete(String[] ids) {
|
public void delete(String[] ids) {
|
||||||
baseDao.deleteBatchIds(Arrays.asList(ids));
|
baseDao.deleteByIds(Arrays.asList(ids));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -82,4 +82,4 @@ public class OtaServiceImpl extends BaseServiceImpl<OtaDao, OtaEntity> implement
|
|||||||
.last("LIMIT 1");
|
.last("LIMIT 1");
|
||||||
return baseDao.selectOne(wrapper);
|
return baseDao.selectOne(wrapper);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-10
@@ -149,8 +149,8 @@ public class RAGFlowAdapter extends KnowledgeBaseAdapter {
|
|||||||
log.info("=== [RAGFlow] 获取文档列表: datasetId={} ===", datasetId);
|
log.info("=== [RAGFlow] 获取文档列表: datasetId={} ===", datasetId);
|
||||||
|
|
||||||
// 使用 Jackson 将 DTO 转为 Map 作为查询参数
|
// 使用 Jackson 将 DTO 转为 Map 作为查询参数
|
||||||
@SuppressWarnings("unchecked")
|
Map<String, Object> params = objectMapper.convertValue(req, new TypeReference<Map<String, Object>>() {
|
||||||
Map<String, Object> params = objectMapper.convertValue(req, Map.class);
|
});
|
||||||
|
|
||||||
Map<String, Object> response = getClient().get("/api/v1/datasets/" + datasetId + "/documents", params);
|
Map<String, Object> response = getClient().get("/api/v1/datasets/" + datasetId + "/documents", params);
|
||||||
|
|
||||||
@@ -174,13 +174,12 @@ public class RAGFlowAdapter extends KnowledgeBaseAdapter {
|
|||||||
.pageSize(1)
|
.pageSize(1)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
Map<String, Object> params = objectMapper.convertValue(req, new TypeReference<Map<String, Object>>() {
|
||||||
Map<String, Object> params = objectMapper.convertValue(req, Map.class);
|
});
|
||||||
Map<String, Object> response = getClient().get("/api/v1/datasets/" + datasetId + "/documents", params);
|
Map<String, Object> response = getClient().get("/api/v1/datasets/" + datasetId + "/documents", params);
|
||||||
|
|
||||||
Object dataObj = response.get("data");
|
Object dataObj = response.get("data");
|
||||||
if (dataObj instanceof Map) {
|
if (dataObj instanceof Map<?, ?> dataMap) {
|
||||||
Map<String, Object> dataMap = (Map<String, Object>) dataObj;
|
|
||||||
List<?> documents = (List<?>) dataMap.get("docs");
|
List<?> documents = (List<?>) dataMap.get("docs");
|
||||||
if (documents != null && !documents.isEmpty()) {
|
if (documents != null && !documents.isEmpty()) {
|
||||||
return objectMapper.convertValue(documents.get(0), DocumentDTO.InfoVO.class);
|
return objectMapper.convertValue(documents.get(0), DocumentDTO.InfoVO.class);
|
||||||
@@ -567,8 +566,8 @@ public class RAGFlowAdapter extends KnowledgeBaseAdapter {
|
|||||||
return new PageData<>(new ArrayList<>(), 0);
|
return new PageData<>(new ArrayList<>(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Object> dataMap = (Map<String, Object>) dataObj;
|
Map<?, ?> dataMap = Map.class.cast(dataObj);
|
||||||
List<Map<String, Object>> documents = (List<Map<String, Object>>) dataMap.get("docs");
|
List<?> documents = List.class.cast(dataMap.get("docs"));
|
||||||
if (documents == null || documents.isEmpty()) {
|
if (documents == null || documents.isEmpty()) {
|
||||||
// RAGFlow 明确返回了空文档列表,这是合法的"真空"
|
// RAGFlow 明确返回了空文档列表,这是合法的"真空"
|
||||||
return new PageData<>(new ArrayList<>(), 0);
|
return new PageData<>(new ArrayList<>(), 0);
|
||||||
@@ -679,7 +678,9 @@ public class RAGFlowAdapter extends KnowledgeBaseAdapter {
|
|||||||
dto.setChunkMethod(info.getChunkMethod().name().toLowerCase());
|
dto.setChunkMethod(info.getChunkMethod().name().toLowerCase());
|
||||||
}
|
}
|
||||||
if (info.getParserConfig() != null) {
|
if (info.getParserConfig() != null) {
|
||||||
dto.setParserConfig(objectMapper.convertValue(info.getParserConfig(), Map.class));
|
dto.setParserConfig(objectMapper.convertValue(info.getParserConfig(),
|
||||||
|
new TypeReference<Map<String, Object>>() {
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
return dto;
|
return dto;
|
||||||
@@ -717,4 +718,4 @@ public class RAGFlowAdapter extends KnowledgeBaseAdapter {
|
|||||||
return !multipartFile.isEmpty();
|
return !multipartFile.isEmpty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-10
@@ -116,13 +116,11 @@ public class OpenAIStyleLLMServiceImpl implements LLMService {
|
|||||||
Map<String, Object> requestBody = new HashMap<>();
|
Map<String, Object> requestBody = new HashMap<>();
|
||||||
requestBody.put("model", model != null ? model : "gpt-3.5-turbo");
|
requestBody.put("model", model != null ? model : "gpt-3.5-turbo");
|
||||||
|
|
||||||
Map<String, Object>[] messages = new Map[1];
|
|
||||||
Map<String, Object> message = new HashMap<>();
|
Map<String, Object> message = new HashMap<>();
|
||||||
message.put("role", "user");
|
message.put("role", "user");
|
||||||
message.put("content", prompt);
|
message.put("content", prompt);
|
||||||
messages[0] = message;
|
|
||||||
|
|
||||||
requestBody.put("messages", messages);
|
requestBody.put("messages", List.of(message));
|
||||||
requestBody.put("temperature", temperature != null ? temperature : 0.7);
|
requestBody.put("temperature", temperature != null ? temperature : 0.7);
|
||||||
requestBody.put("max_tokens", maxTokens != null ? maxTokens : 2000);
|
requestBody.put("max_tokens", maxTokens != null ? maxTokens : 2000);
|
||||||
|
|
||||||
@@ -212,13 +210,11 @@ public class OpenAIStyleLLMServiceImpl implements LLMService {
|
|||||||
Map<String, Object> requestBody = new HashMap<>();
|
Map<String, Object> requestBody = new HashMap<>();
|
||||||
requestBody.put("model", model != null ? model : "gpt-3.5-turbo");
|
requestBody.put("model", model != null ? model : "gpt-3.5-turbo");
|
||||||
|
|
||||||
Map<String, Object>[] messages = new Map[1];
|
|
||||||
Map<String, Object> message = new HashMap<>();
|
Map<String, Object> message = new HashMap<>();
|
||||||
message.put("role", "user");
|
message.put("role", "user");
|
||||||
message.put("content", prompt);
|
message.put("content", prompt);
|
||||||
messages[0] = message;
|
|
||||||
|
|
||||||
requestBody.put("messages", messages);
|
requestBody.put("messages", List.of(message));
|
||||||
requestBody.put("temperature", 0.2);
|
requestBody.put("temperature", 0.2);
|
||||||
requestBody.put("max_tokens", 2000);
|
requestBody.put("max_tokens", 2000);
|
||||||
|
|
||||||
@@ -368,13 +364,11 @@ public class OpenAIStyleLLMServiceImpl implements LLMService {
|
|||||||
Map<String, Object> requestBody = new HashMap<>();
|
Map<String, Object> requestBody = new HashMap<>();
|
||||||
requestBody.put("model", model != null ? model : "gpt-3.5-turbo");
|
requestBody.put("model", model != null ? model : "gpt-3.5-turbo");
|
||||||
|
|
||||||
Map<String, Object>[] messages = new Map[1];
|
|
||||||
Map<String, Object> message = new HashMap<>();
|
Map<String, Object> message = new HashMap<>();
|
||||||
message.put("role", "user");
|
message.put("role", "user");
|
||||||
message.put("content", prompt);
|
message.put("content", prompt);
|
||||||
messages[0] = message;
|
|
||||||
|
|
||||||
requestBody.put("messages", messages);
|
requestBody.put("messages", List.of(message));
|
||||||
requestBody.put("temperature", 0.3);
|
requestBody.put("temperature", 0.3);
|
||||||
requestBody.put("max_tokens", 50);
|
requestBody.put("max_tokens", 50);
|
||||||
|
|
||||||
@@ -422,4 +416,4 @@ public class OpenAIStyleLLMServiceImpl implements LLMService {
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-5
@@ -362,14 +362,14 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
|
|||||||
if (SensitiveDataUtils.isSensitiveField(key)) {
|
if (SensitiveDataUtils.isSensitiveField(key)) {
|
||||||
|
|
||||||
if (value instanceof String && !SensitiveDataUtils.isMaskedValue((String) value)) {
|
if (value instanceof String && !SensitiveDataUtils.isMaskedValue((String) value)) {
|
||||||
updatedJson.put(key, value);
|
updatedJson.set(key, value);
|
||||||
}
|
}
|
||||||
} else if (value instanceof JSONObject) {
|
} else if (value instanceof JSONObject) {
|
||||||
// 递归处理嵌套JSON
|
// 递归处理嵌套JSON
|
||||||
mergeJson(updatedJson, key, (JSONObject) value);
|
mergeJson(updatedJson, key, (JSONObject) value);
|
||||||
} else {
|
} else {
|
||||||
// 非敏感字段直接更新
|
// 非敏感字段直接更新
|
||||||
updatedJson.put(key, value);
|
updatedJson.set(key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -405,7 +405,7 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
|
|||||||
|
|
||||||
// 如果 original 中不存在 key,创建一个新的 JSON 对象
|
// 如果 original 中不存在 key,创建一个新的 JSON 对象
|
||||||
if (!original.containsKey(key)) {
|
if (!original.containsKey(key)) {
|
||||||
original.put(key, new JSONObject());
|
original.set(key, new JSONObject());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取 original 中的子对象
|
// 获取 original 中的子对象
|
||||||
@@ -420,7 +420,7 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
|
|||||||
log.warn("mergeJson: key '{}' 的值不是 JSONObject 类型 (实际类型:{}),将创建新对象",
|
log.warn("mergeJson: key '{}' 的值不是 JSONObject 类型 (实际类型:{}),将创建新对象",
|
||||||
key, originalValue != null ? originalValue.getClass().getSimpleName() : "null");
|
key, originalValue != null ? originalValue.getClass().getSimpleName() : "null");
|
||||||
originalChild = new JSONObject();
|
originalChild = new JSONObject();
|
||||||
original.put(key, originalChild);
|
original.set(key, originalChild);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (String childKey : updated.keySet()) {
|
for (String childKey : updated.keySet()) {
|
||||||
@@ -430,7 +430,7 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
|
|||||||
} else {
|
} else {
|
||||||
if (!SensitiveDataUtils.isSensitiveField(childKey) ||
|
if (!SensitiveDataUtils.isSensitiveField(childKey) ||
|
||||||
(childValue instanceof String && !isMaskedValue((String) childValue))) {
|
(childValue instanceof String && !isMaskedValue((String) childValue))) {
|
||||||
originalChild.put(childKey, childValue);
|
originalChild.set(childKey, childValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -164,7 +164,7 @@ public class ModelProviderServiceImpl extends BaseServiceImpl<ModelProviderDao,
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void delete(List<String> ids) {
|
public void delete(List<String> ids) {
|
||||||
if (modelProviderDao.deleteBatchIds(ids) == 0) {
|
if (modelProviderDao.deleteByIds(ids) == 0) {
|
||||||
throw new RenException(ErrorCode.DELETE_DATA_FAILED);
|
throw new RenException(ErrorCode.DELETE_DATA_FAILED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,16 +4,19 @@ import java.util.HashMap;
|
|||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.shiro.mgt.SecurityManager;
|
|
||||||
import org.apache.shiro.session.mgt.SessionManager;
|
import org.apache.shiro.session.mgt.SessionManager;
|
||||||
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
|
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
|
||||||
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
|
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
|
||||||
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
|
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
|
||||||
import org.apache.shiro.web.config.ShiroFilterConfiguration;
|
import org.apache.shiro.web.config.ShiroFilterConfiguration;
|
||||||
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
|
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
|
||||||
|
import org.apache.shiro.web.mgt.WebSecurityManager;
|
||||||
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
|
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
|
||||||
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.context.annotation.Role;
|
||||||
|
|
||||||
import jakarta.servlet.Filter;
|
import jakarta.servlet.Filter;
|
||||||
import xiaozhi.modules.security.oauth2.Oauth2Filter;
|
import xiaozhi.modules.security.oauth2.Oauth2Filter;
|
||||||
@@ -39,7 +42,7 @@ public class ShiroConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Bean("securityManager")
|
@Bean("securityManager")
|
||||||
public SecurityManager securityManager(Oauth2Realm oAuth2Realm, SessionManager sessionManager) {
|
public WebSecurityManager securityManager(Oauth2Realm oAuth2Realm, SessionManager sessionManager) {
|
||||||
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
|
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
|
||||||
securityManager.setRealm(oAuth2Realm);
|
securityManager.setRealm(oAuth2Realm);
|
||||||
securityManager.setSessionManager(sessionManager);
|
securityManager.setSessionManager(sessionManager);
|
||||||
@@ -48,7 +51,8 @@ public class ShiroConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Bean("shiroFilter")
|
@Bean("shiroFilter")
|
||||||
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager, SysParamsService sysParamsService) {
|
public static ShiroFilterFactoryBean shirFilter(@Lazy WebSecurityManager securityManager,
|
||||||
|
@Lazy SysParamsService sysParamsService) {
|
||||||
ShiroFilterConfiguration config = new ShiroFilterConfiguration();
|
ShiroFilterConfiguration config = new ShiroFilterConfiguration();
|
||||||
config.setFilterOncePerRequest(true);
|
config.setFilterOncePerRequest(true);
|
||||||
|
|
||||||
@@ -101,12 +105,14 @@ public class ShiroConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Bean("lifecycleBeanPostProcessor")
|
@Bean("lifecycleBeanPostProcessor")
|
||||||
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
|
public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
|
||||||
return new LifecycleBeanPostProcessor();
|
return new LifecycleBeanPostProcessor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
|
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
|
public static AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
|
||||||
|
@Lazy WebSecurityManager securityManager) {
|
||||||
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
|
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
|
||||||
advisor.setSecurityManager(securityManager);
|
advisor.setSecurityManager(securityManager);
|
||||||
return advisor;
|
return advisor;
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ public class SysUserDTO implements Serializable {
|
|||||||
@NotNull(message = "{id.require}", groups = UpdateGroup.class)
|
@NotNull(message = "{id.require}", groups = UpdateGroup.class)
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
@Schema(description = "用户名", required = true)
|
@Schema(description = "用户名", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
@NotBlank(message = "{sysuser.username.require}", groups = DefaultGroup.class)
|
@NotBlank(message = "{sysuser.username.require}", groups = DefaultGroup.class)
|
||||||
private String username;
|
private String username;
|
||||||
|
|
||||||
@@ -40,14 +40,14 @@ public class SysUserDTO implements Serializable {
|
|||||||
@NotBlank(message = "{sysuser.password.require}", groups = AddGroup.class)
|
@NotBlank(message = "{sysuser.password.require}", groups = AddGroup.class)
|
||||||
private String password;
|
private String password;
|
||||||
|
|
||||||
@Schema(description = "姓名", required = true)
|
@Schema(description = "姓名", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
@NotBlank(message = "{sysuser.realname.require}", groups = DefaultGroup.class)
|
@NotBlank(message = "{sysuser.realname.require}", groups = DefaultGroup.class)
|
||||||
private String realName;
|
private String realName;
|
||||||
|
|
||||||
@Schema(description = "头像")
|
@Schema(description = "头像")
|
||||||
private String headUrl;
|
private String headUrl;
|
||||||
|
|
||||||
@Schema(description = "性别 0:男 1:女 2:保密", required = true)
|
@Schema(description = "性别 0:男 1:女 2:保密", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
@Range(min = 0, max = 2, message = "{sysuser.gender.range}", groups = DefaultGroup.class)
|
@Range(min = 0, max = 2, message = "{sysuser.gender.range}", groups = DefaultGroup.class)
|
||||||
private Integer gender;
|
private Integer gender;
|
||||||
|
|
||||||
@@ -58,11 +58,11 @@ public class SysUserDTO implements Serializable {
|
|||||||
@Schema(description = "手机号")
|
@Schema(description = "手机号")
|
||||||
private String mobile;
|
private String mobile;
|
||||||
|
|
||||||
@Schema(description = "部门ID", required = true)
|
@Schema(description = "部门ID", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
@NotNull(message = "{sysuser.deptId.require}", groups = DefaultGroup.class)
|
@NotNull(message = "{sysuser.deptId.require}", groups = DefaultGroup.class)
|
||||||
private Long deptId;
|
private Long deptId;
|
||||||
|
|
||||||
@Schema(description = "状态 0:停用 1:正常", required = true)
|
@Schema(description = "状态 0:停用 1:正常", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
@Range(min = 0, max = 1, message = "{sysuser.status.range}", groups = DefaultGroup.class)
|
@Range(min = 0, max = 1, message = "{sysuser.status.range}", groups = DefaultGroup.class)
|
||||||
private Integer status;
|
private Integer status;
|
||||||
|
|
||||||
@@ -84,4 +84,4 @@ public class SysUserDTO implements Serializable {
|
|||||||
@Schema(description = "部门名称")
|
@Schema(description = "部门名称")
|
||||||
private String deptName;
|
private String deptName;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-3
@@ -20,6 +20,7 @@ import xiaozhi.common.redis.RedisKeys;
|
|||||||
import xiaozhi.common.redis.RedisUtils;
|
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.ToolUtil;
|
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;
|
||||||
@@ -138,7 +139,7 @@ public class SysDictDataServiceImpl extends BaseServiceImpl<SysDictDataDao, SysD
|
|||||||
|
|
||||||
// 设置更新者和创建者名称
|
// 设置更新者和创建者名称
|
||||||
if (!userIds.isEmpty()) {
|
if (!userIds.isEmpty()) {
|
||||||
List<SysUserEntity> sysUserEntities = sysUserDao.selectBatchIds(userIds);
|
List<SysUserEntity> sysUserEntities = sysUserDao.selectByIds(userIds);
|
||||||
// 把List转成Map,Map<Long, String>
|
// 把List转成Map,Map<Long, String>
|
||||||
Map<Long, String> userNameMap = sysUserEntities.stream().collect(Collectors.toMap(SysUserEntity::getId,
|
Map<Long, String> userNameMap = sysUserEntities.stream().collect(Collectors.toMap(SysUserEntity::getId,
|
||||||
SysUserEntity::getUsername, (existing, replacement) -> existing));
|
SysUserEntity::getUsername, (existing, replacement) -> existing));
|
||||||
@@ -170,7 +171,7 @@ public class SysDictDataServiceImpl extends BaseServiceImpl<SysDictDataDao, SysD
|
|||||||
|
|
||||||
// 先从Redis获取缓存
|
// 先从Redis获取缓存
|
||||||
String key = RedisKeys.getDictDataByTypeKey(dictType);
|
String key = RedisKeys.getDictDataByTypeKey(dictType);
|
||||||
List<SysDictDataItem> cachedData = (List<SysDictDataItem>) redisUtils.get(key);
|
List<SysDictDataItem> cachedData = JsonUtils.toList(redisUtils.get(key), SysDictDataItem.class);
|
||||||
if (cachedData != null) {
|
if (cachedData != null) {
|
||||||
return cachedData;
|
return cachedData;
|
||||||
}
|
}
|
||||||
@@ -185,4 +186,4 @@ public class SysDictDataServiceImpl extends BaseServiceImpl<SysDictDataDao, SysD
|
|||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -128,7 +128,7 @@ public class SysDictTypeServiceImpl extends BaseServiceImpl<SysDictTypeDao, SysD
|
|||||||
|
|
||||||
// 设置更新者和创建者名称
|
// 设置更新者和创建者名称
|
||||||
if (!userIds.isEmpty()) {
|
if (!userIds.isEmpty()) {
|
||||||
List<SysUserEntity> sysUserEntities = sysUserDao.selectBatchIds(userIds);
|
List<SysUserEntity> sysUserEntities = sysUserDao.selectByIds(userIds);
|
||||||
// 把List转成Map,Map<Long, String>
|
// 把List转成Map,Map<Long, String>
|
||||||
Map<Long, String> userNameMap = sysUserEntities.stream().collect(Collectors.toMap(SysUserEntity::getId,
|
Map<Long, String> userNameMap = sysUserEntities.stream().collect(Collectors.toMap(SysUserEntity::getId,
|
||||||
SysUserEntity::getUsername, (existing, replacement) -> existing));
|
SysUserEntity::getUsername, (existing, replacement) -> existing));
|
||||||
@@ -151,4 +151,4 @@ public class SysDictTypeServiceImpl extends BaseServiceImpl<SysDictTypeDao, SysD
|
|||||||
throw new RenException(ErrorCode.DICT_TYPE_DUPLICATE);
|
throw new RenException(ErrorCode.DICT_TYPE_DUPLICATE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-12
@@ -287,10 +287,10 @@ public class SysParamsServiceImpl extends BaseServiceImpl<SysParamsDao, SysParam
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (StringUtils.isNotBlank(currentConfig)) {
|
if (StringUtils.isNotBlank(currentConfig)) {
|
||||||
currentMap = JsonUtils.parseObject(currentConfig, Map.class);
|
currentMap = JsonUtils.parseMap(currentConfig);
|
||||||
}
|
}
|
||||||
if (StringUtils.isNotBlank(configJson)) {
|
if (StringUtils.isNotBlank(configJson)) {
|
||||||
newMap = JsonUtils.parseObject(configJson, Map.class);
|
newMap = JsonUtils.parseMap(configJson);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RenException(ErrorCode.PARAM_JSON_INVALID);
|
throw new RenException(ErrorCode.PARAM_JSON_INVALID);
|
||||||
@@ -298,8 +298,8 @@ public class SysParamsServiceImpl extends BaseServiceImpl<SysParamsDao, SysParam
|
|||||||
|
|
||||||
// 检查addressBook功能是否被关闭
|
// 检查addressBook功能是否被关闭
|
||||||
if (currentMap != null && newMap != null) {
|
if (currentMap != null && newMap != null) {
|
||||||
Map<String, Object> currentFeatures = (Map<String, Object>) currentMap.get("features");
|
Map<?, ?> currentFeatures = Map.class.cast(currentMap.get("features"));
|
||||||
Map<String, Object> newFeatures = (Map<String, Object>) newMap.get("features");
|
Map<?, ?> newFeatures = Map.class.cast(newMap.get("features"));
|
||||||
|
|
||||||
if (currentFeatures != null && newFeatures != null) {
|
if (currentFeatures != null && newFeatures != null) {
|
||||||
Object currentAddressBookObj = currentFeatures.get("addressBook");
|
Object currentAddressBookObj = currentFeatures.get("addressBook");
|
||||||
@@ -308,16 +308,14 @@ public class SysParamsServiceImpl extends BaseServiceImpl<SysParamsDao, SysParam
|
|||||||
Boolean currentEnabled = false;
|
Boolean currentEnabled = false;
|
||||||
Boolean newEnabled = false;
|
Boolean newEnabled = false;
|
||||||
|
|
||||||
if (currentAddressBookObj instanceof Map) {
|
if (currentAddressBookObj instanceof Map<?, ?> currentAddressBook) {
|
||||||
Map<String, Object> currentAddressBook = (Map<String, Object>) currentAddressBookObj;
|
Object enabled = currentAddressBook.get("enabled");
|
||||||
currentEnabled = currentAddressBook.get("enabled") != null
|
currentEnabled = enabled != null ? Boolean.class.cast(enabled) : false;
|
||||||
? (Boolean) currentAddressBook.get("enabled") : false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newAddressBookObj instanceof Map) {
|
if (newAddressBookObj instanceof Map<?, ?> newAddressBook) {
|
||||||
Map<String, Object> newAddressBook = (Map<String, Object>) newAddressBookObj;
|
Object enabled = newAddressBook.get("enabled");
|
||||||
newEnabled = newAddressBook.get("enabled") != null
|
newEnabled = enabled != null ? Boolean.class.cast(enabled) : false;
|
||||||
? (Boolean) newAddressBook.get("enabled") : false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果之前是启用状态,现在被禁用,删除所有call_device插件
|
// 如果之前是启用状态,现在被禁用,删除所有call_device插件
|
||||||
|
|||||||
+1
-1
@@ -117,7 +117,7 @@ public class TimbreServiceImpl extends BaseServiceImpl<TimbreDao, TimbreEntity>
|
|||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void delete(String[] ids) {
|
public void delete(String[] ids) {
|
||||||
baseDao.deleteBatchIds(Arrays.asList(ids));
|
baseDao.deleteByIds(Arrays.asList(ids));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+1
-1
@@ -127,7 +127,7 @@ public class VoiceCloneServiceImpl extends BaseServiceImpl<VoiceCloneDao, VoiceC
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void delete(String[] ids) {
|
public void delete(String[] ids) {
|
||||||
baseDao.deleteBatchIds(Arrays.asList(ids));
|
baseDao.deleteByIds(Arrays.asList(ids));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,20 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<configuration>
|
<configuration>
|
||||||
<!-- 启用JansiConsoleAppender以确保控制台输出有颜色 -->
|
|
||||||
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
|
|
||||||
|
|
||||||
<!-- 确保日志目录存在 -->
|
|
||||||
<timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss"/>
|
|
||||||
|
|
||||||
<!-- 定义日志文件存储位置 -->
|
<!-- 定义日志文件存储位置 -->
|
||||||
<property name="LOG_HOME" value="./logs" />
|
<property name="LOG_HOME" value="./logs" />
|
||||||
|
|
||||||
<!-- 使用自定义的初始化监听器确保日志目录存在 -->
|
|
||||||
<define name="LOGBACK_DIR_CHECK" class="ch.qos.logback.core.property.FileExistsPropertyDefiner">
|
|
||||||
<path>${LOG_HOME}</path>
|
|
||||||
<createIfMissing>true</createIfMissing>
|
|
||||||
</define>
|
|
||||||
|
|
||||||
<!-- 引入Spring Boot默认配置 -->
|
<!-- 引入Spring Boot默认配置 -->
|
||||||
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
|
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
WHERE mac_address = #{macAddress} AND target_mac = #{targetMac}
|
WHERE mac_address = #{macAddress} AND target_mac = #{targetMac}
|
||||||
</update>
|
</update>
|
||||||
|
|
||||||
<insert id="insert">
|
<insert id="insertAddressBook">
|
||||||
INSERT INTO ai_device_address_book (mac_address, target_mac, alias, has_permission, creator, create_date, updater, update_date)
|
INSERT INTO ai_device_address_book (mac_address, target_mac, alias, has_permission, creator, create_date, updater, update_date)
|
||||||
VALUES (#{macAddress}, #{targetMac}, #{alias}, #{hasPermission}, #{creator}, NOW(), #{updater}, NOW())
|
VALUES (#{macAddress}, #{targetMac}, #{alias}, #{hasPermission}, #{creator}, NOW(), #{updater}, NOW())
|
||||||
</insert>
|
</insert>
|
||||||
@@ -36,4 +36,4 @@
|
|||||||
#{mac}
|
#{mac}
|
||||||
</foreach>
|
</foreach>
|
||||||
</delete>
|
</delete>
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package xiaozhi.common.redis;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||||
|
|
||||||
|
import xiaozhi.common.utils.JsonUtils;
|
||||||
|
import xiaozhi.modules.sys.vo.SysDictDataItem;
|
||||||
|
|
||||||
|
class RedisSerializationTest {
|
||||||
|
private final RedisSerializer<Object> serializer = RedisSerializer.json();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void serverConfigRoundTripRestoresStringKeyedMaps() {
|
||||||
|
Map<String, Object> features = new HashMap<>();
|
||||||
|
features.put("addressBook", Map.of("enabled", true));
|
||||||
|
Map<String, Object> config = new HashMap<>();
|
||||||
|
config.put("features", features);
|
||||||
|
|
||||||
|
Object restored = roundTrip(config);
|
||||||
|
Map<String, Object> restoredConfig = JsonUtils.toStringObjectMap(restored);
|
||||||
|
Map<String, Object> restoredFeatures = JsonUtils.toStringObjectMap(restoredConfig.get("features"));
|
||||||
|
Map<String, Object> addressBook = JsonUtils.toStringObjectMap(restoredFeatures.get("addressBook"));
|
||||||
|
|
||||||
|
assertEquals(true, addressBook.get("enabled"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void dictionaryListRoundTripRestoresDtoElements() {
|
||||||
|
SysDictDataItem item = new SysDictDataItem();
|
||||||
|
item.setName("enabled");
|
||||||
|
item.setKey("1");
|
||||||
|
|
||||||
|
Object restored = roundTrip(new ArrayList<>(List.of(item)));
|
||||||
|
List<SysDictDataItem> restoredItems = JsonUtils.toList(restored, SysDictDataItem.class);
|
||||||
|
|
||||||
|
assertInstanceOf(SysDictDataItem.class, restoredItems.get(0));
|
||||||
|
assertEquals("enabled", restoredItems.get(0).getName());
|
||||||
|
assertEquals("1", restoredItems.get(0).getKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object roundTrip(Object value) {
|
||||||
|
byte[] bytes = serializer.serialize(value);
|
||||||
|
assertNotNull(bytes);
|
||||||
|
Object restored = serializer.deserialize(bytes);
|
||||||
|
assertNotNull(restored);
|
||||||
|
return restored;
|
||||||
|
}
|
||||||
|
}
|
||||||
+20
-1
@@ -22,6 +22,7 @@ import java.util.function.BiFunction;
|
|||||||
|
|
||||||
import org.apache.ibatis.binding.MapperMethod;
|
import org.apache.ibatis.binding.MapperMethod;
|
||||||
import org.apache.ibatis.executor.BatchResult;
|
import org.apache.ibatis.executor.BatchResult;
|
||||||
|
import org.apache.ibatis.logging.nologging.NoLoggingImpl;
|
||||||
import org.apache.ibatis.mapping.MappedStatement;
|
import org.apache.ibatis.mapping.MappedStatement;
|
||||||
import org.apache.ibatis.session.ExecutorType;
|
import org.apache.ibatis.session.ExecutorType;
|
||||||
import org.apache.ibatis.session.SqlSession;
|
import org.apache.ibatis.session.SqlSession;
|
||||||
@@ -57,7 +58,8 @@ class BaseServiceImplTest {
|
|||||||
when(sqlSessionFactory.openSession(ExecutorType.BATCH)).thenReturn(sqlSession);
|
when(sqlSessionFactory.openSession(ExecutorType.BATCH)).thenReturn(sqlSession);
|
||||||
when(sqlSession.insert(eq(INSERT_STATEMENT), any(TestEntity.class))).thenReturn(1);
|
when(sqlSession.insert(eq(INSERT_STATEMENT), any(TestEntity.class))).thenReturn(1);
|
||||||
when(sqlSession.flushStatements())
|
when(sqlSession.flushStatements())
|
||||||
.thenReturn(List.of(batchResult(1, 1)), List.of(batchResult(1)));
|
.thenReturn(List.of(batchResult(1, 1)))
|
||||||
|
.thenReturn(List.of(batchResult(1)));
|
||||||
|
|
||||||
TestEntity first = new TestEntity(1L);
|
TestEntity first = new TestEntity(1L);
|
||||||
TestEntity second = new TestEntity(2L);
|
TestEntity second = new TestEntity(2L);
|
||||||
@@ -126,6 +128,19 @@ class BaseServiceImplTest {
|
|||||||
verifyNoInteractions(sqlSessionFactory);
|
verifyNoInteractions(sqlSessionFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void deleteBatchIdsDelegatesToCompatibleApiAndPreservesAffectedRowResult() {
|
||||||
|
TestMapper mapper = mock(TestMapper.class);
|
||||||
|
List<Long> ids = List.of(1L, 2L);
|
||||||
|
service.baseDao = mapper;
|
||||||
|
when(mapper.deleteByIds(ids)).thenReturn(2).thenReturn(0);
|
||||||
|
|
||||||
|
assertTrue(service.deleteBatchIds(ids));
|
||||||
|
assertFalse(service.deleteBatchIds(ids));
|
||||||
|
|
||||||
|
verify(mapper, times(2)).deleteByIds(ids);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void activeTransactionSynchronizationUsesTransactionAwareCommitAndLifecycle() {
|
void activeTransactionSynchronizationUsesTransactionAwareCommitAndLifecycle() {
|
||||||
SqlSessionFactory sqlSessionFactory = mock(SqlSessionFactory.class);
|
SqlSessionFactory sqlSessionFactory = mock(SqlSessionFactory.class);
|
||||||
@@ -170,6 +185,10 @@ class BaseServiceImplTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static class TestService extends BaseServiceImpl<TestMapper, TestEntity> {
|
private static class TestService extends BaseServiceImpl<TestMapper, TestEntity> {
|
||||||
|
private TestService() {
|
||||||
|
log = new NoLoggingImpl(TestService.class.getName());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getSqlStatement(SqlMethod sqlMethod) {
|
protected String getSqlStatement(SqlMethod sqlMethod) {
|
||||||
return switch (sqlMethod) {
|
return switch (sqlMethod) {
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package xiaozhi.common.utils;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
class JsonUtilsTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void parsesTypedMapsAndPreservesNestedCollectionShapes() {
|
||||||
|
Map<String, Object> map = JsonUtils.parseMap(
|
||||||
|
"{\"enabled\":true,\"nested\":{\"items\":[{\"name\":\"tool\"}]}}");
|
||||||
|
Map<?, ?> nested = assertInstanceOf(Map.class, map.get("nested"));
|
||||||
|
List<?> items = assertInstanceOf(List.class, nested.get("items"));
|
||||||
|
Map<?, ?> item = assertInstanceOf(Map.class, items.get(0));
|
||||||
|
|
||||||
|
assertEquals(true, map.get("enabled"));
|
||||||
|
assertEquals("tool", item.get("name"));
|
||||||
|
|
||||||
|
List<Map<String, Object>> maps = JsonUtils.parseMapList(
|
||||||
|
"[{\"name\":\"first\",\"values\":[1,2]},{\"name\":\"second\"}]");
|
||||||
|
assertEquals("first", maps.get(0).get("name"));
|
||||||
|
assertEquals(List.of(1, 2), maps.get(0).get("values"));
|
||||||
|
assertEquals("second", maps.get(1).get("name"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void checkedConvertersAreShallowMutableCopies() {
|
||||||
|
List<Object> nested = new ArrayList<>(List.of(Map.of("name", "tool")));
|
||||||
|
Map<String, Object> source = new LinkedHashMap<>();
|
||||||
|
source.put("nested", nested);
|
||||||
|
|
||||||
|
Map<String, Object> map = JsonUtils.toStringObjectMap(source);
|
||||||
|
List<Map<String, Object>> maps = JsonUtils.toStringObjectMapList(List.of(source));
|
||||||
|
List<String> strings = JsonUtils.toList(Arrays.asList("a", null), String.class);
|
||||||
|
|
||||||
|
assertSame(nested, map.get("nested"));
|
||||||
|
assertSame(nested, maps.get(0).get("nested"));
|
||||||
|
map.put("enabled", true);
|
||||||
|
maps.add(Map.of("name", "second"));
|
||||||
|
strings.add("b");
|
||||||
|
assertEquals(true, map.get("enabled"));
|
||||||
|
assertEquals(2, maps.size());
|
||||||
|
assertEquals(Arrays.asList("a", null, "b"), strings);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void preservesNullInputs() {
|
||||||
|
assertNull(JsonUtils.parseMap(""));
|
||||||
|
assertNull(JsonUtils.parseMapList(null));
|
||||||
|
assertNull(JsonUtils.toStringObjectMap(null));
|
||||||
|
assertNull(JsonUtils.toStringObjectMapList(null));
|
||||||
|
assertNull(JsonUtils.toList(null, String.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void rejectsUnexpectedJsonShapesAndRuntimeTypes() {
|
||||||
|
assertThrows(RuntimeException.class, () -> JsonUtils.parseMap("[]"));
|
||||||
|
assertThrows(RuntimeException.class, () -> JsonUtils.parseMapList("{}"));
|
||||||
|
assertThrows(ClassCastException.class, () -> JsonUtils.toStringObjectMap(List.of()));
|
||||||
|
assertThrows(ClassCastException.class, () -> JsonUtils.toStringObjectMap(Map.of(1, "value")));
|
||||||
|
assertThrows(ClassCastException.class, () -> JsonUtils.toStringObjectMapList(List.of("value")));
|
||||||
|
assertThrows(ClassCastException.class, () -> JsonUtils.toStringObjectMapList(Map.of()));
|
||||||
|
assertThrows(ClassCastException.class, () -> JsonUtils.toList(List.of(1), String.class));
|
||||||
|
assertThrows(ClassCastException.class, () -> JsonUtils.toList(Map.of(), String.class));
|
||||||
|
}
|
||||||
|
}
|
||||||
+96
@@ -0,0 +1,96 @@
|
|||||||
|
package xiaozhi.modules.config.service.impl;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotSame;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyMap;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
|
import xiaozhi.common.redis.RedisKeys;
|
||||||
|
import xiaozhi.common.redis.RedisUtils;
|
||||||
|
import xiaozhi.modules.agent.dao.AgentVoicePrintDao;
|
||||||
|
import xiaozhi.modules.agent.service.AgentContextProviderService;
|
||||||
|
import xiaozhi.modules.agent.service.AgentMcpAccessPointService;
|
||||||
|
import xiaozhi.modules.agent.service.AgentPluginMappingService;
|
||||||
|
import xiaozhi.modules.agent.service.AgentService;
|
||||||
|
import xiaozhi.modules.agent.service.AgentTemplateService;
|
||||||
|
import xiaozhi.modules.correctword.service.CorrectWordFileService;
|
||||||
|
import xiaozhi.modules.device.service.DeviceService;
|
||||||
|
import xiaozhi.modules.model.service.ModelConfigService;
|
||||||
|
import xiaozhi.modules.sys.dto.SysParamsDTO;
|
||||||
|
import xiaozhi.modules.sys.service.SysParamsService;
|
||||||
|
import xiaozhi.modules.timbre.service.TimbreService;
|
||||||
|
import xiaozhi.modules.voiceclone.service.VoiceCloneService;
|
||||||
|
|
||||||
|
class ConfigServiceImplTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void cachedServerConfigIsCheckedAsAStringKeyedMapWithoutChangingNestedValues() {
|
||||||
|
RedisUtils redisUtils = mock(RedisUtils.class);
|
||||||
|
Map<String, Object> nested = new HashMap<>();
|
||||||
|
nested.put("enabled", true);
|
||||||
|
Map<String, Object> cached = new HashMap<>();
|
||||||
|
cached.put("features", nested);
|
||||||
|
when(redisUtils.get(RedisKeys.getServerConfigKey())).thenReturn(cached);
|
||||||
|
|
||||||
|
ConfigServiceImpl service = newService(mock(SysParamsService.class), redisUtils);
|
||||||
|
|
||||||
|
Map<String, Object> result = service.getConfig(true);
|
||||||
|
|
||||||
|
assertNotSame(cached, result);
|
||||||
|
assertSame(nested, result.get("features"));
|
||||||
|
assertEquals(true, ((Map<?, ?>) result.get("features")).get("enabled"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void nestedSystemParametersStillShareAndPopulateTheSameConfigBranch() {
|
||||||
|
SysParamsService sysParamsService = mock(SysParamsService.class);
|
||||||
|
SysParamsDTO enabled = parameter("server.features.enabled", "true", "boolean");
|
||||||
|
SysParamsDTO labels = parameter("server.features.labels", "first;second", "array");
|
||||||
|
when(sysParamsService.list(anyMap())).thenReturn(List.of(enabled, labels));
|
||||||
|
ConfigServiceImpl service = newService(sysParamsService, mock(RedisUtils.class));
|
||||||
|
Map<String, Object> config = new HashMap<>();
|
||||||
|
|
||||||
|
Object returned = ReflectionTestUtils.invokeMethod(service, "buildConfig", config);
|
||||||
|
|
||||||
|
assertSame(config, returned);
|
||||||
|
Map<?, ?> server = assertInstanceOf(Map.class, config.get("server"));
|
||||||
|
Map<?, ?> features = assertInstanceOf(Map.class, server.get("features"));
|
||||||
|
assertEquals(true, features.get("enabled"));
|
||||||
|
assertEquals(List.of("first", "second"), features.get("labels"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SysParamsDTO parameter(String code, String value, String type) {
|
||||||
|
SysParamsDTO parameter = new SysParamsDTO();
|
||||||
|
parameter.setParamCode(code);
|
||||||
|
parameter.setParamValue(value);
|
||||||
|
parameter.setValueType(type);
|
||||||
|
return parameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ConfigServiceImpl newService(SysParamsService sysParamsService, RedisUtils redisUtils) {
|
||||||
|
return new ConfigServiceImpl(
|
||||||
|
sysParamsService,
|
||||||
|
mock(DeviceService.class),
|
||||||
|
mock(ModelConfigService.class),
|
||||||
|
mock(AgentService.class),
|
||||||
|
mock(AgentTemplateService.class),
|
||||||
|
redisUtils,
|
||||||
|
mock(TimbreService.class),
|
||||||
|
mock(AgentPluginMappingService.class),
|
||||||
|
mock(AgentMcpAccessPointService.class),
|
||||||
|
mock(AgentContextProviderService.class),
|
||||||
|
mock(VoiceCloneService.class),
|
||||||
|
mock(AgentVoicePrintDao.class),
|
||||||
|
mock(CorrectWordFileService.class));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
package xiaozhi.modules.device;
|
package xiaozhi.modules.device;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.DisplayName;
|
import org.junit.jupiter.api.DisplayName;
|
||||||
@@ -11,6 +10,8 @@ import org.springframework.boot.test.context.SpringBootTest;
|
|||||||
import org.springframework.test.context.ActiveProfiles;
|
import org.springframework.test.context.ActiveProfiles;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import xiaozhi.common.exception.ErrorCode;
|
||||||
|
import xiaozhi.common.exception.RenException;
|
||||||
import xiaozhi.common.redis.RedisUtils;
|
import xiaozhi.common.redis.RedisUtils;
|
||||||
import xiaozhi.modules.sys.dto.SysUserDTO;
|
import xiaozhi.modules.sys.dto.SysUserDTO;
|
||||||
import xiaozhi.modules.sys.service.SysUserService;
|
import xiaozhi.modules.sys.service.SysUserService;
|
||||||
@@ -27,11 +28,13 @@ public class DeviceTest {
|
|||||||
private SysUserService sysUserService;
|
private SysUserService sysUserService;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSaveUser() {
|
public void testRejectWeakPassword() {
|
||||||
SysUserDTO userDTO = new SysUserDTO();
|
SysUserDTO userDTO = new SysUserDTO();
|
||||||
userDTO.setUsername("test");
|
userDTO.setUsername("test");
|
||||||
userDTO.setPassword(UUID.randomUUID().toString());
|
userDTO.setPassword("weak-password-123");
|
||||||
sysUserService.save(userDTO);
|
|
||||||
|
RenException exception = Assertions.assertThrows(RenException.class, () -> sysUserService.save(userDTO));
|
||||||
|
Assertions.assertEquals(ErrorCode.PASSWORD_WEAK_ERROR, exception.getCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -70,4 +73,4 @@ public class DeviceTest {
|
|||||||
|
|
||||||
log.info("测试完成");
|
log.info("测试完成");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+46
@@ -0,0 +1,46 @@
|
|||||||
|
package xiaozhi.modules.device.service.impl;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
|
||||||
|
import xiaozhi.common.redis.RedisUtils;
|
||||||
|
import xiaozhi.modules.device.dao.DeviceAddressBookDao;
|
||||||
|
import xiaozhi.modules.device.entity.DeviceAddressBookEntity;
|
||||||
|
import xiaozhi.modules.device.service.DeviceService;
|
||||||
|
import xiaozhi.modules.sys.service.SysParamsService;
|
||||||
|
|
||||||
|
class DeviceAddressBookServiceImplTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void newEntryUsesTheDedicatedAddressBookInsertMapping() {
|
||||||
|
DeviceAddressBookDao addressBookDao = mock(DeviceAddressBookDao.class);
|
||||||
|
RedisUtils redisUtils = mock(RedisUtils.class);
|
||||||
|
DeviceService deviceService = mock(DeviceService.class);
|
||||||
|
SysParamsService sysParamsService = mock(SysParamsService.class);
|
||||||
|
DeviceAddressBookServiceImpl service = new DeviceAddressBookServiceImpl(
|
||||||
|
addressBookDao, redisUtils, deviceService, sysParamsService);
|
||||||
|
when(addressBookDao.selectOne(any())).thenReturn(null);
|
||||||
|
when(addressBookDao.selectList(null)).thenReturn(List.of());
|
||||||
|
|
||||||
|
service.saveOrUpdate("00:11:22:33:44:55", "00:11:22:33:44:66", "living-room", true);
|
||||||
|
|
||||||
|
ArgumentCaptor<DeviceAddressBookEntity> entityCaptor = ArgumentCaptor.forClass(DeviceAddressBookEntity.class);
|
||||||
|
verify(addressBookDao).insertAddressBook(entityCaptor.capture());
|
||||||
|
DeviceAddressBookEntity entity = entityCaptor.getValue();
|
||||||
|
assertEquals("00:11:22:33:44:55", entity.getMacAddress());
|
||||||
|
assertEquals("00:11:22:33:44:66", entity.getTargetMac());
|
||||||
|
assertEquals("living-room", entity.getAlias());
|
||||||
|
assertTrue(entity.getHasPermission());
|
||||||
|
verify(addressBookDao, never()).insert(any(DeviceAddressBookEntity.class));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package xiaozhi.modules.security.config;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
|
||||||
|
import org.apache.shiro.session.mgt.SessionManager;
|
||||||
|
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
|
||||||
|
import org.apache.shiro.web.mgt.WebSecurityManager;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
|
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.context.annotation.Role;
|
||||||
|
|
||||||
|
import xiaozhi.modules.security.oauth2.Oauth2Realm;
|
||||||
|
import xiaozhi.modules.sys.service.SysParamsService;
|
||||||
|
|
||||||
|
class ShiroConfigTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void beanPostProcessorFactoriesDoNotInstantiateShiroConfigOrBusinessDependenciesEarly() throws Exception {
|
||||||
|
Method lifecycleFactory = ShiroConfig.class.getDeclaredMethod("lifecycleBeanPostProcessor");
|
||||||
|
Method filterFactory = ShiroConfig.class.getDeclaredMethod(
|
||||||
|
"shirFilter", WebSecurityManager.class, SysParamsService.class);
|
||||||
|
|
||||||
|
assertTrue(Modifier.isStatic(lifecycleFactory.getModifiers()));
|
||||||
|
assertTrue(BeanPostProcessor.class.isAssignableFrom(ShiroFilterFactoryBean.class));
|
||||||
|
assertTrue(Modifier.isStatic(filterFactory.getModifiers()));
|
||||||
|
Lazy securityManagerLazy = filterFactory.getParameters()[0].getAnnotation(Lazy.class);
|
||||||
|
Lazy sysParamsServiceLazy = filterFactory.getParameters()[1].getAnnotation(Lazy.class);
|
||||||
|
assertNotNull(securityManagerLazy);
|
||||||
|
assertNotNull(sysParamsServiceLazy);
|
||||||
|
assertTrue(securityManagerLazy.value());
|
||||||
|
assertTrue(sysParamsServiceLazy.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void authorizationAdvisorIsInfrastructureAndDefersItsSecurityManager() throws Exception {
|
||||||
|
Method advisorFactory = ShiroConfig.class.getDeclaredMethod(
|
||||||
|
"authorizationAttributeSourceAdvisor", WebSecurityManager.class);
|
||||||
|
|
||||||
|
assertTrue(Modifier.isStatic(advisorFactory.getModifiers()));
|
||||||
|
Lazy securityManagerLazy = advisorFactory.getParameters()[0].getAnnotation(Lazy.class);
|
||||||
|
assertNotNull(securityManagerLazy);
|
||||||
|
assertTrue(securityManagerLazy.value());
|
||||||
|
assertEquals(BeanDefinition.ROLE_INFRASTRUCTURE, advisorFactory.getAnnotation(Role.class).value());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void securityManagerRetainsTheWebSecurityContractRequiredByShiroFilter() throws Exception {
|
||||||
|
Method securityManagerFactory = ShiroConfig.class.getDeclaredMethod(
|
||||||
|
"securityManager", Oauth2Realm.class, SessionManager.class);
|
||||||
|
|
||||||
|
assertEquals(WebSecurityManager.class, securityManagerFactory.getReturnType());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +1,23 @@
|
|||||||
package xiaozhi.modules.sys;
|
package xiaozhi.modules.sys;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.test.context.ActiveProfiles;
|
import org.springframework.test.context.ActiveProfiles;
|
||||||
|
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import xiaozhi.common.exception.ErrorCode;
|
||||||
|
import xiaozhi.common.exception.RenException;
|
||||||
import xiaozhi.modules.security.controller.LoginController;
|
import xiaozhi.modules.security.controller.LoginController;
|
||||||
import xiaozhi.modules.security.dto.LoginDTO;
|
import xiaozhi.modules.security.dto.LoginDTO;
|
||||||
import xiaozhi.modules.security.dto.SmsVerificationDTO;
|
import xiaozhi.modules.security.dto.SmsVerificationDTO;
|
||||||
import xiaozhi.modules.sys.dto.RetrievePasswordDTO;
|
import xiaozhi.modules.sys.dto.RetrievePasswordDTO;
|
||||||
|
import xiaozhi.modules.sys.service.SysUserService;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@SpringBootTest
|
@SpringBootTest
|
||||||
@@ -19,12 +27,19 @@ class loginControllerTest {
|
|||||||
@Autowired
|
@Autowired
|
||||||
LoginController loginController;
|
LoginController loginController;
|
||||||
|
|
||||||
|
@MockitoBean
|
||||||
|
SysUserService sysUserService;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRegister() {
|
public void testRegister() {
|
||||||
|
when(sysUserService.getAllowUserRegister()).thenReturn(false);
|
||||||
|
|
||||||
LoginDTO loginDTO = new LoginDTO();
|
LoginDTO loginDTO = new LoginDTO();
|
||||||
loginDTO.setUsername("手机号码");
|
loginDTO.setUsername("手机号码");
|
||||||
loginDTO.setPassword("密码");
|
loginDTO.setPassword("密码");
|
||||||
loginController.register(loginDTO);
|
|
||||||
|
RenException exception = assertThrows(RenException.class, () -> loginController.register(loginDTO));
|
||||||
|
assertEquals(ErrorCode.USER_REGISTER_DISABLED, exception.getCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -54,4 +69,4 @@ class loginControllerTest {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
package xiaozhi.modules.sys.service.impl;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotSame;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import xiaozhi.common.redis.RedisKeys;
|
||||||
|
import xiaozhi.common.redis.RedisUtils;
|
||||||
|
import xiaozhi.modules.sys.dao.SysUserDao;
|
||||||
|
import xiaozhi.modules.sys.vo.SysDictDataItem;
|
||||||
|
|
||||||
|
class SysDictDataServiceImplTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void cachedDictionaryItemsAreCheckedAndReturnedAsDtos() {
|
||||||
|
RedisUtils redisUtils = mock(RedisUtils.class);
|
||||||
|
SysDictDataItem item = new SysDictDataItem();
|
||||||
|
item.setName("enabled");
|
||||||
|
item.setKey("1");
|
||||||
|
List<SysDictDataItem> cached = new ArrayList<>(List.of(item));
|
||||||
|
when(redisUtils.get(RedisKeys.getDictDataByTypeKey("status"))).thenReturn(cached);
|
||||||
|
SysDictDataServiceImpl service = new SysDictDataServiceImpl(mock(SysUserDao.class), redisUtils);
|
||||||
|
|
||||||
|
List<SysDictDataItem> result = service.getDictDataByType("status");
|
||||||
|
|
||||||
|
assertNotSame(cached, result);
|
||||||
|
assertEquals(List.of(item), result);
|
||||||
|
}
|
||||||
|
}
|
||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
package xiaozhi.modules.sys.service.impl;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
|
import xiaozhi.common.constant.Constant;
|
||||||
|
import xiaozhi.modules.agent.service.AgentPluginMappingService;
|
||||||
|
import xiaozhi.modules.sys.dao.SysParamsDao;
|
||||||
|
import xiaozhi.modules.sys.redis.SysParamsRedis;
|
||||||
|
|
||||||
|
class SysParamsServiceImplTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void disablingAddressBookStillDeletesItsSystemPlugin() {
|
||||||
|
SysParamsRedis sysParamsRedis = mock(SysParamsRedis.class);
|
||||||
|
AgentPluginMappingService pluginMappingService = mock(AgentPluginMappingService.class);
|
||||||
|
SysParamsDao sysParamsDao = mock(SysParamsDao.class);
|
||||||
|
SysParamsServiceImpl service = new SysParamsServiceImpl(sysParamsRedis, pluginMappingService);
|
||||||
|
ReflectionTestUtils.setField(service, "baseDao", sysParamsDao);
|
||||||
|
|
||||||
|
String currentConfig = "{\"features\":{\"addressBook\":{\"enabled\":true}}}";
|
||||||
|
String newConfig = "{\"features\":{\"addressBook\":{\"enabled\":false}}}";
|
||||||
|
when(sysParamsDao.getValueByCode(Constant.SYSTEM_WEB_MENU)).thenReturn(currentConfig);
|
||||||
|
when(sysParamsDao.updateValueByCode(Constant.SYSTEM_WEB_MENU, newConfig)).thenReturn(1);
|
||||||
|
|
||||||
|
service.updateSystemWebMenu(newConfig);
|
||||||
|
|
||||||
|
verify(pluginMappingService).deleteByPluginId("SYSTEM_PLUGIN_CALL_DEVICE");
|
||||||
|
verify(sysParamsDao).updateValueByCode(Constant.SYSTEM_WEB_MENU, newConfig);
|
||||||
|
verify(sysParamsRedis).set(Constant.SYSTEM_WEB_MENU, newConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
spring:
|
spring:
|
||||||
|
messages:
|
||||||
|
encoding: UTF-8
|
||||||
|
basename: i18n/messages
|
||||||
data:
|
data:
|
||||||
redis:
|
redis:
|
||||||
host: localhost
|
host: localhost
|
||||||
@@ -19,4 +22,4 @@ renren:
|
|||||||
logging:
|
logging:
|
||||||
level:
|
level:
|
||||||
xiaozhi.modules.device: DEBUG
|
xiaozhi.modules.device: DEBUG
|
||||||
org.springframework.data.redis: DEBUG
|
org.springframework.data.redis: DEBUG
|
||||||
|
|||||||
@@ -118,8 +118,17 @@ async def process_intent_result(
|
|||||||
|
|
||||||
请根据以上信息回答用户的问题:{original_text}"""
|
请根据以上信息回答用户的问题:{original_text}"""
|
||||||
|
|
||||||
response = conn.intent.replyResult(context_prompt, original_text)
|
# 使用异步调用避免阻塞事件循环,影响其他设备的音频播放
|
||||||
speak_txt(conn, response)
|
try:
|
||||||
|
response = asyncio.run_coroutine_threadsafe(
|
||||||
|
conn.intent.replyResult(context_prompt, original_text),
|
||||||
|
conn.loop,
|
||||||
|
).result()
|
||||||
|
except Exception as e:
|
||||||
|
conn.logger.bind(tag=TAG).error(f"LLM生成回复失败: {e}")
|
||||||
|
response = None
|
||||||
|
if response:
|
||||||
|
speak_txt(conn, response)
|
||||||
|
|
||||||
conn.executor.submit(process_context_result)
|
conn.executor.submit(process_context_result)
|
||||||
return True
|
return True
|
||||||
@@ -184,7 +193,15 @@ async def process_intent_result(
|
|||||||
elif result.action == Action.REQLLM: # 调用函数后再请求llm生成回复
|
elif result.action == Action.REQLLM: # 调用函数后再请求llm生成回复
|
||||||
text = result.result
|
text = result.result
|
||||||
conn.dialogue.put(Message(role="tool", content=text))
|
conn.dialogue.put(Message(role="tool", content=text))
|
||||||
llm_result = conn.intent.replyResult(text, original_text)
|
# 使用异步调用避免阻塞事件循环,影响其他设备的音频播放
|
||||||
|
try:
|
||||||
|
llm_result = asyncio.run_coroutine_threadsafe(
|
||||||
|
conn.intent.replyResult(text, original_text),
|
||||||
|
conn.loop,
|
||||||
|
).result()
|
||||||
|
except Exception as e:
|
||||||
|
conn.logger.bind(tag=TAG).error(f"LLM生成回复失败: {e}")
|
||||||
|
llm_result = text
|
||||||
if llm_result is None:
|
if llm_result is None:
|
||||||
llm_result = text
|
llm_result = text
|
||||||
speak_txt(conn, llm_result)
|
speak_txt(conn, llm_result)
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import asyncio
|
||||||
from typing import List, Dict, TYPE_CHECKING
|
from typing import List, Dict, TYPE_CHECKING
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@@ -120,12 +121,18 @@ class IntentProvider(IntentProviderBase):
|
|||||||
)
|
)
|
||||||
return prompt
|
return prompt
|
||||||
|
|
||||||
def replyResult(self, text: str, original_text: str):
|
async def replyResult(self, text: str, original_text: str):
|
||||||
|
"""使用 asyncio.to_thread 避免阻塞事件循环"""
|
||||||
try:
|
try:
|
||||||
llm_result = self.llm.response_no_stream(
|
user_prompt = (
|
||||||
|
"请根据以上内容,像人类一样说话的口吻回复用户,要求简洁,请直接返回结果。用户现在说:"
|
||||||
|
+ original_text
|
||||||
|
)
|
||||||
|
# 使用 to_thread 将同步阻塞调用放到线程池中执行,不阻塞事件循环
|
||||||
|
llm_result = await asyncio.to_thread(
|
||||||
|
self.llm.response_no_stream,
|
||||||
system_prompt=text,
|
system_prompt=text,
|
||||||
user_prompt="请根据以上内容,像人类一样说话的口吻回复用户,要求简洁,请直接返回结果。用户现在说:"
|
user_prompt=user_prompt,
|
||||||
+ original_text,
|
|
||||||
)
|
)
|
||||||
return llm_result
|
return llm_result
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -207,8 +214,11 @@ class IntentProvider(IntentProviderBase):
|
|||||||
logger.bind(tag=TAG).debug(f"开始LLM意图识别调用, 模型: {model_info}")
|
logger.bind(tag=TAG).debug(f"开始LLM意图识别调用, 模型: {model_info}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
intent = self.llm.response_no_stream(
|
# 使用 to_thread 将同步阻塞调用放到线程池中,避免阻塞事件循环
|
||||||
system_prompt=prompt_music, user_prompt=user_prompt
|
intent = await asyncio.to_thread(
|
||||||
|
self.llm.response_no_stream,
|
||||||
|
system_prompt=prompt_music,
|
||||||
|
user_prompt=user_prompt,
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.bind(tag=TAG).error(f"Error in intent detection LLM call: {e}")
|
logger.bind(tag=TAG).error(f"Error in intent detection LLM call: {e}")
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import random
|
import random
|
||||||
import httpx
|
import httpx
|
||||||
from markitdown import MarkItDown
|
from io import BytesIO
|
||||||
|
from markitdown import MarkItDown, StreamInfo
|
||||||
from config.logger import setup_logging
|
from config.logger import setup_logging
|
||||||
from plugins_func.register import register_function, ToolType, ActionResponse, Action
|
from plugins_func.register import register_function, ToolType, ActionResponse, Action
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
@@ -144,7 +145,14 @@ async def fetch_news_detail(url):
|
|||||||
|
|
||||||
# 使用MarkItDown清理HTML内容
|
# 使用MarkItDown清理HTML内容
|
||||||
md = MarkItDown(enable_plugins=False)
|
md = MarkItDown(enable_plugins=False)
|
||||||
result = md.convert(response)
|
result = md.convert_stream(
|
||||||
|
BytesIO(response.content),
|
||||||
|
stream_info=StreamInfo(
|
||||||
|
mimetype="text/html",
|
||||||
|
extension=".html",
|
||||||
|
charset=response.encoding or "utf-8",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
# 获取清理后的文本内容
|
# 获取清理后的文本内容
|
||||||
clean_text = result.text_content
|
clean_text = result.text_content
|
||||||
|
|||||||
Reference in New Issue
Block a user