Merge pull request #3297 from xinnan-tech/fix/manager-api-compiler-warnings

fix(manager-api): 清理编译器告警
This commit is contained in:
CGD
2026-07-22 16:22:46 +08:00
committed by GitHub
36 changed files with 489 additions and 105 deletions
+10
View File
@@ -272,6 +272,16 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</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>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
@@ -67,7 +67,7 @@ public class DataFilterInterceptor implements InnerInterceptor {
private String getSelect(String buildSql, DataScope scope) {
try {
Select select = (Select) CCJSqlParserUtil.parse(buildSql);
PlainSelect plainSelect = (PlainSelect) select.getSelectBody();
PlainSelect plainSelect = select.getPlainSelect();
Expression expression = plainSelect.getWhere();
if (expression == null) {
@@ -82,4 +82,4 @@ public class DataFilterInterceptor implements InnerInterceptor {
return buildSql;
}
}
}
}
@@ -81,8 +81,8 @@ public abstract class BaseServiceImpl<M extends BaseMapper<T>, T> implements Bas
// 处理排序字段
if (orderField instanceof String) {
orderFields.add((String) orderField);
} else if (orderField instanceof List) {
orderFields.addAll((List<String>) orderField);
} else if (orderField instanceof List<?> fields) {
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);
}
protected Class<M> currentMapperClass() {
return (Class<M>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseServiceImpl.class, 0);
protected Class<?> currentMapperClass() {
return ReflectionKit.getSuperClassGenericType(this.getClass(), BaseServiceImpl.class, 0);
}
@Override
@SuppressWarnings("unchecked")
public Class<T> currentModelClass() {
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
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>
implements CrudService<T, D> {
@SuppressWarnings("unchecked")
protected Class<D> currentDtoClass() {
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
public void delete(Serializable[] ids) {
baseDao.deleteBatchIds(Arrays.asList(ids));
baseDao.deleteByIds(Arrays.asList(ids));
}
}
}
@@ -1,7 +1,9 @@
package xiaozhi.common.utils;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -16,6 +18,10 @@ import cn.hutool.core.util.StrUtil;
*/
public class JsonUtils {
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) {
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);
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) {
result.put(key, maskSensitiveFields((JSONObject) value));
result.set(key, maskSensitiveFields((JSONObject) value));
} 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;
}
// 去掉'|"|;|\字符
str = StringUtils.replace(str, "'", "");
str = StringUtils.replace(str, "\"", "");
str = StringUtils.replace(str, ";", "");
str = StringUtils.replace(str, "\\", "");
str = str.replace("'", "");
str = str.replace("\"", "");
str = str.replace(";", "");
str = str.replace("\\", "");
// 转换成小写
str = str.toLowerCase();
@@ -34,6 +34,7 @@ import xiaozhi.common.page.PageData;
import xiaozhi.common.redis.RedisKeys;
import xiaozhi.common.redis.RedisUtils;
import xiaozhi.common.user.UserDetail;
import xiaozhi.common.utils.JsonUtils;
import xiaozhi.common.utils.Result;
import xiaozhi.common.utils.ResultUtils;
import xiaozhi.modules.agent.dto.AgentChatHistoryDTO;
@@ -340,8 +341,8 @@ public class AgentController {
@RequiresPermissions("sys:role:normal")
public Result<Void> saveAgentTags(@PathVariable String id, @RequestBody Map<String, Object> params) {
requireAgentPermission(id);
List<String> tagIds = (List<String>) params.get("tagIds");
List<String> tagNames = (List<String>) params.get("tagNames");
List<String> tagIds = JsonUtils.toList(params.get("tagIds"), String.class);
List<String> tagNames = JsonUtils.toList(params.get("tagNames"), String.class);
AgentUpdateDTO dto = new AgentUpdateDTO();
dto.setTagIds(tagIds);
dto.setTagNames(tagNames);
@@ -39,7 +39,7 @@ public class AgentDTO {
private String systemPrompt;
@Schema(description = "总结记忆", example = "构建可生长的动态记忆网络,在有限空间内保留关键信息的同时,智能维护信息演变轨迹\n" +
"根据对话记录,总结user的重要信息,以便在未来的对话中提供更个性化的服务", required = false)
"根据对话记录,总结user的重要信息,以便在未来的对话中提供更个性化的服务", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String summaryMemory;
@Schema(description = "最后连接时间", example = "2024-03-20 10:00:00")
@@ -50,4 +50,4 @@ public class AgentDTO {
@Schema(description = "标签列表")
private List<AgentTagDTO> tags;
}
}
@@ -14,6 +14,6 @@ public class AgentMemoryDTO implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "总结记忆", example = "构建可生长的动态记忆网络,在有限空间内保留关键信息的同时,智能维护信息演变轨迹\n" +
"根据对话记录,总结user的重要信息,以便在未来的对话中提供更个性化的服务", required = false)
"根据对话记录,总结user的重要信息,以便在未来的对话中提供更个性化的服务", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String summaryMemory;
}
}
@@ -39,10 +39,10 @@ public class AgentUpdateDTO implements Serializable {
@Schema(description = "小模型标识", example = "slm_model_02", nullable = true)
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;
@Schema(description = "语音合成模型标识", example = "tts_model_02", required = false)
@Schema(description = "语音合成模型标识", example = "tts_model_02", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String ttsModelId;
@Schema(description = "音色标识", example = "voice_02", nullable = true)
@@ -74,7 +74,7 @@ public class AgentEntity {
private String systemPrompt;
@Schema(description = "总结记忆", example = "构建可生长的动态记忆网络,在有限空间内保留关键信息的同时,智能维护信息演变轨迹\n" +
"根据对话记录,总结user的重要信息,以便在未来的对话中提供更个性化的服务", required = false)
"根据对话记录,总结user的重要信息,以便在未来的对话中提供更个性化的服务", requiredMode = Schema.RequiredMode.NOT_REQUIRED)
private String summaryMemory;
@Schema(description = "语言编码")
@@ -97,4 +97,4 @@ public class AgentEntity {
@Schema(description = "更新时间")
private Date updatedAt;
}
}
@@ -167,7 +167,7 @@ public class AgentChatHistoryServiceImpl extends CrudRepository<AiAgentChatHisto
// 尝试解析为 JSON
try {
Map<String, Object> jsonMap = JsonUtils.parseObject(content, Map.class);
Map<String, Object> jsonMap = JsonUtils.parseMap(content);
if (jsonMap != null && jsonMap.containsKey("content")) {
Object contentObj = jsonMap.get("content");
return contentObj != null ? contentObj.toString() : content;
@@ -76,7 +76,7 @@ public class AgentMcpAccessPointServiceImpl implements AgentMcpAccessPointServic
// 等待初始化响应 (id=1) - 移除固定延迟,改为响应驱动
List<String> initResponses = client.listenerWithoutClose(response -> {
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"))) {
// 检查是否有result字段,表示初始化成功
return jsonMap.containsKey("result") && !jsonMap.containsKey("error");
@@ -92,7 +92,7 @@ public class AgentMcpAccessPointServiceImpl implements AgentMcpAccessPointServic
boolean initSucceeded = false;
for (String response : initResponses) {
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.containsKey("result")) {
log.info("MCP初始化成功,智能体ID: {}", id);
@@ -123,7 +123,7 @@ public class AgentMcpAccessPointServiceImpl implements AgentMcpAccessPointServic
// 等待工具列表响应 (id=2)
List<String> toolsResponses = client.listener(response -> {
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"));
} catch (Exception e) {
log.warn("解析工具列表响应失败: {}", response, e);
@@ -134,18 +134,18 @@ public class AgentMcpAccessPointServiceImpl implements AgentMcpAccessPointServic
// 处理工具列表响应
for (String response : toolsResponses) {
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"))) {
// 检查是否有result字段
Object resultObj = jsonMap.get("result");
if (resultObj instanceof Map) {
Map<String, Object> resultMap = (Map<String, Object>) resultObj;
if (resultObj instanceof Map<?, ?>) {
Map<String, Object> resultMap = JsonUtils.toStringObjectMap(resultObj);
Object toolsObj = resultMap.get("tools");
if (toolsObj instanceof List) {
List<Map<String, Object>> toolsList = (List<Map<String, Object>>) toolsObj;
if (toolsObj instanceof List<?>) {
List<Map<String, Object>> toolsList = JsonUtils.toStringObjectMapList(toolsObj);
// 提取工具名称列表
List<String> result = toolsList.stream()
.map(tool -> (String) tool.get("name"))
.map(tool -> String.class.cast(tool.get("name")))
.filter(name -> name != null)
.sorted()
.collect(Collectors.toList());
@@ -232,4 +232,4 @@ public class AgentMcpAccessPointServiceImpl implements AgentMcpAccessPointServic
// 加密后成token值
return AESUtils.encrypt(key, json);
}
}
}
@@ -677,7 +677,7 @@ public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> imp
mapping.setPluginId(pluginId);
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) {
for (Map<String, Object> field : fields) {
paramInfo.put((String) field.get("key"), field.get("default"));
@@ -133,7 +133,7 @@ public class AgentTagServiceImpl extends BaseServiceImpl<AgentTagDao, AgentTagEn
}
if (tagIds != null && !tagIds.isEmpty()) {
List<AgentTagEntity> tagIdEntities = baseDao.selectBatchIds(tagIds);
List<AgentTagEntity> tagIdEntities = baseDao.selectByIds(tagIds);
for (AgentTagEntity tag : tagIdEntities) {
if (tag != null && (currentTagNames.contains(tag.getTagName()) ||
newTagNames.contains(tag.getTagName()))) {
@@ -10,7 +10,7 @@ public interface ConfigService {
* @param isCache 是否缓存
* @return 配置信息
*/
Object getConfig(Boolean isCache);
Map<String, Object> getConfig(Boolean isCache);
/**
* 获取智能体模型配置
@@ -28,4 +28,4 @@ public interface ConfigService {
* @return 替换词列表,格式如 ["模板1|模板01", "模板2|模板02"]
*/
List<String> getCorrectWords(String macAddress);
}
}
@@ -65,12 +65,12 @@ public class ConfigServiceImpl implements ConfigService {
private final CorrectWordFileService correctWordFileService;
@Override
public Object getConfig(Boolean isCache) {
public Map<String, Object> getConfig(Boolean isCache) {
if (isCache) {
// 先从Redis获取配置
Object cachedConfig = redisUtils.get(RedisKeys.getServerConfigKey());
if (cachedConfig != null) {
return cachedConfig;
return JsonUtils.toStringObjectMap(cachedConfig);
}
}
@@ -123,7 +123,7 @@ public class ConfigServiceImpl implements ConfigService {
if (isAdminRequest != null && "true".equals(isAdminRequest)) {
// 管理控制台请求,返回getConfig的结果
redisUtils.delete(redisKey); // 使用后清理
return (Map<String, Object>) getConfig(true);
return getConfig(true);
}
// 根据MAC地址查找设备
DeviceEntity device = deviceService.getDeviceByMacAddress(macAddress);
@@ -277,10 +277,10 @@ public class ConfigServiceImpl implements ConfigService {
// 遍历除最后一个key之外的所有key
for (int i = 0; i < keys.length - 1; i++) {
String key = keys[i];
if (!current.containsKey(key)) {
current.put(key, new HashMap<String, Object>());
}
current = (Map<String, Object>) current.get(key);
Object nestedConfig = current.computeIfAbsent(key, ignored -> new HashMap<String, Object>());
Map<String, Object> nestedMap = JsonUtils.toStringObjectMap(nestedConfig);
current.put(key, nestedMap);
current = nestedMap;
}
// 处理最后一个key
@@ -50,6 +50,7 @@ import xiaozhi.common.service.impl.BaseServiceImpl;
import xiaozhi.common.user.UserDetail;
import xiaozhi.common.utils.ConvertUtils;
import xiaozhi.common.utils.DateUtils;
import xiaozhi.common.utils.JsonUtils;
import xiaozhi.common.utils.ToolUtil;
import xiaozhi.modules.device.dao.DeviceDao;
import xiaozhi.modules.device.dto.DeviceManualAddDTO;
@@ -109,7 +110,7 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
String deviceId = (String) cacheDeviceId;
String safeDeviceId = deviceId.replace(":", "_").toLowerCase();
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)) {
throw new RenException(ErrorCode.ACTIVATION_CODE_ERROR);
}
@@ -411,7 +412,7 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
public String geCodeByDeviceId(String 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")) {
String cachedCode = (String) cacheMap.get("activation_code");
return cachedCode;
@@ -56,7 +56,7 @@ public class OtaServiceImpl extends BaseServiceImpl<OtaDao, OtaEntity> implement
@Override
public void delete(String[] ids) {
baseDao.deleteBatchIds(Arrays.asList(ids));
baseDao.deleteByIds(Arrays.asList(ids));
}
@Override
@@ -82,4 +82,4 @@ public class OtaServiceImpl extends BaseServiceImpl<OtaDao, OtaEntity> implement
.last("LIMIT 1");
return baseDao.selectOne(wrapper);
}
}
}
@@ -149,8 +149,8 @@ public class RAGFlowAdapter extends KnowledgeBaseAdapter {
log.info("=== [RAGFlow] 获取文档列表: datasetId={} ===", datasetId);
// 使用 Jackson 将 DTO 转为 Map 作为查询参数
@SuppressWarnings("unchecked")
Map<String, Object> params = objectMapper.convertValue(req, Map.class);
Map<String, Object> params = objectMapper.convertValue(req, new TypeReference<Map<String, Object>>() {
});
Map<String, Object> response = getClient().get("/api/v1/datasets/" + datasetId + "/documents", params);
@@ -174,13 +174,12 @@ public class RAGFlowAdapter extends KnowledgeBaseAdapter {
.pageSize(1)
.build();
@SuppressWarnings("unchecked")
Map<String, Object> params = objectMapper.convertValue(req, Map.class);
Map<String, Object> params = objectMapper.convertValue(req, new TypeReference<Map<String, Object>>() {
});
Map<String, Object> response = getClient().get("/api/v1/datasets/" + datasetId + "/documents", params);
Object dataObj = response.get("data");
if (dataObj instanceof Map) {
Map<String, Object> dataMap = (Map<String, Object>) dataObj;
if (dataObj instanceof Map<?, ?> dataMap) {
List<?> documents = (List<?>) dataMap.get("docs");
if (documents != null && !documents.isEmpty()) {
return objectMapper.convertValue(documents.get(0), DocumentDTO.InfoVO.class);
@@ -567,8 +566,8 @@ public class RAGFlowAdapter extends KnowledgeBaseAdapter {
return new PageData<>(new ArrayList<>(), 0);
}
Map<String, Object> dataMap = (Map<String, Object>) dataObj;
List<Map<String, Object>> documents = (List<Map<String, Object>>) dataMap.get("docs");
Map<?, ?> dataMap = Map.class.cast(dataObj);
List<?> documents = List.class.cast(dataMap.get("docs"));
if (documents == null || documents.isEmpty()) {
// RAGFlow 明确返回了空文档列表,这是合法的"真空"
return new PageData<>(new ArrayList<>(), 0);
@@ -679,7 +678,9 @@ public class RAGFlowAdapter extends KnowledgeBaseAdapter {
dto.setChunkMethod(info.getChunkMethod().name().toLowerCase());
}
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;
@@ -717,4 +718,4 @@ public class RAGFlowAdapter extends KnowledgeBaseAdapter {
return !multipartFile.isEmpty();
}
}
}
}
@@ -116,13 +116,11 @@ public class OpenAIStyleLLMServiceImpl implements LLMService {
Map<String, Object> requestBody = new HashMap<>();
requestBody.put("model", model != null ? model : "gpt-3.5-turbo");
Map<String, Object>[] messages = new Map[1];
Map<String, Object> message = new HashMap<>();
message.put("role", "user");
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("max_tokens", maxTokens != null ? maxTokens : 2000);
@@ -212,13 +210,11 @@ public class OpenAIStyleLLMServiceImpl implements LLMService {
Map<String, Object> requestBody = new HashMap<>();
requestBody.put("model", model != null ? model : "gpt-3.5-turbo");
Map<String, Object>[] messages = new Map[1];
Map<String, Object> message = new HashMap<>();
message.put("role", "user");
message.put("content", prompt);
messages[0] = message;
requestBody.put("messages", messages);
requestBody.put("messages", List.of(message));
requestBody.put("temperature", 0.2);
requestBody.put("max_tokens", 2000);
@@ -368,13 +364,11 @@ public class OpenAIStyleLLMServiceImpl implements LLMService {
Map<String, Object> requestBody = new HashMap<>();
requestBody.put("model", model != null ? model : "gpt-3.5-turbo");
Map<String, Object>[] messages = new Map[1];
Map<String, Object> message = new HashMap<>();
message.put("role", "user");
message.put("content", prompt);
messages[0] = message;
requestBody.put("messages", messages);
requestBody.put("messages", List.of(message));
requestBody.put("temperature", 0.3);
requestBody.put("max_tokens", 50);
@@ -422,4 +416,4 @@ public class OpenAIStyleLLMServiceImpl implements LLMService {
return null;
}
}
}
@@ -362,14 +362,14 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
if (SensitiveDataUtils.isSensitiveField(key)) {
if (value instanceof String && !SensitiveDataUtils.isMaskedValue((String) value)) {
updatedJson.put(key, value);
updatedJson.set(key, value);
}
} else if (value instanceof JSONObject) {
// 递归处理嵌套JSON
mergeJson(updatedJson, key, (JSONObject) value);
} else {
// 非敏感字段直接更新
updatedJson.put(key, value);
updatedJson.set(key, value);
}
}
@@ -405,7 +405,7 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
// 如果 original 中不存在 key,创建一个新的 JSON 对象
if (!original.containsKey(key)) {
original.put(key, new JSONObject());
original.set(key, new JSONObject());
}
// 获取 original 中的子对象
@@ -420,7 +420,7 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
log.warn("mergeJson: key '{}' 的值不是 JSONObject 类型 (实际类型:{}),将创建新对象",
key, originalValue != null ? originalValue.getClass().getSimpleName() : "null");
originalChild = new JSONObject();
original.put(key, originalChild);
original.set(key, originalChild);
}
for (String childKey : updated.keySet()) {
@@ -430,7 +430,7 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
} else {
if (!SensitiveDataUtils.isSensitiveField(childKey) ||
(childValue instanceof String && !isMaskedValue((String) childValue))) {
originalChild.put(childKey, childValue);
originalChild.set(childKey, childValue);
}
}
}
@@ -164,7 +164,7 @@ public class ModelProviderServiceImpl extends BaseServiceImpl<ModelProviderDao,
@Override
public void delete(List<String> ids) {
if (modelProviderDao.deleteBatchIds(ids) == 0) {
if (modelProviderDao.deleteByIds(ids) == 0) {
throw new RenException(ErrorCode.DELETE_DATA_FAILED);
}
}
@@ -31,7 +31,7 @@ public class SysUserDTO implements Serializable {
@NotNull(message = "{id.require}", groups = UpdateGroup.class)
private Long id;
@Schema(description = "用户名", required = true)
@Schema(description = "用户名", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{sysuser.username.require}", groups = DefaultGroup.class)
private String username;
@@ -40,14 +40,14 @@ public class SysUserDTO implements Serializable {
@NotBlank(message = "{sysuser.password.require}", groups = AddGroup.class)
private String password;
@Schema(description = "姓名", required = true)
@Schema(description = "姓名", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{sysuser.realname.require}", groups = DefaultGroup.class)
private String realName;
@Schema(description = "头像")
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)
private Integer gender;
@@ -58,11 +58,11 @@ public class SysUserDTO implements Serializable {
@Schema(description = "手机号")
private String mobile;
@Schema(description = "部门ID", required = true)
@Schema(description = "部门ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "{sysuser.deptId.require}", groups = DefaultGroup.class)
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)
private Integer status;
@@ -84,4 +84,4 @@ public class SysUserDTO implements Serializable {
@Schema(description = "部门名称")
private String deptName;
}
}
@@ -20,6 +20,7 @@ import xiaozhi.common.redis.RedisKeys;
import xiaozhi.common.redis.RedisUtils;
import xiaozhi.common.service.impl.BaseServiceImpl;
import xiaozhi.common.utils.ConvertUtils;
import xiaozhi.common.utils.JsonUtils;
import xiaozhi.common.utils.ToolUtil;
import xiaozhi.modules.sys.dao.SysDictDataDao;
import xiaozhi.modules.sys.dao.SysUserDao;
@@ -138,7 +139,7 @@ public class SysDictDataServiceImpl extends BaseServiceImpl<SysDictDataDao, SysD
// 设置更新者和创建者名称
if (!userIds.isEmpty()) {
List<SysUserEntity> sysUserEntities = sysUserDao.selectBatchIds(userIds);
List<SysUserEntity> sysUserEntities = sysUserDao.selectByIds(userIds);
// 把List转成MapMap<Long, String>
Map<Long, String> userNameMap = sysUserEntities.stream().collect(Collectors.toMap(SysUserEntity::getId,
SysUserEntity::getUsername, (existing, replacement) -> existing));
@@ -170,7 +171,7 @@ public class SysDictDataServiceImpl extends BaseServiceImpl<SysDictDataDao, SysD
// 先从Redis获取缓存
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) {
return cachedData;
}
@@ -185,4 +186,4 @@ public class SysDictDataServiceImpl extends BaseServiceImpl<SysDictDataDao, SysD
return data;
}
}
}
@@ -128,7 +128,7 @@ public class SysDictTypeServiceImpl extends BaseServiceImpl<SysDictTypeDao, SysD
// 设置更新者和创建者名称
if (!userIds.isEmpty()) {
List<SysUserEntity> sysUserEntities = sysUserDao.selectBatchIds(userIds);
List<SysUserEntity> sysUserEntities = sysUserDao.selectByIds(userIds);
// 把List转成MapMap<Long, String>
Map<Long, String> userNameMap = sysUserEntities.stream().collect(Collectors.toMap(SysUserEntity::getId,
SysUserEntity::getUsername, (existing, replacement) -> existing));
@@ -151,4 +151,4 @@ public class SysDictTypeServiceImpl extends BaseServiceImpl<SysDictTypeDao, SysD
throw new RenException(ErrorCode.DICT_TYPE_DUPLICATE);
}
}
}
}
@@ -287,10 +287,10 @@ public class SysParamsServiceImpl extends BaseServiceImpl<SysParamsDao, SysParam
try {
if (StringUtils.isNotBlank(currentConfig)) {
currentMap = JsonUtils.parseObject(currentConfig, Map.class);
currentMap = JsonUtils.parseMap(currentConfig);
}
if (StringUtils.isNotBlank(configJson)) {
newMap = JsonUtils.parseObject(configJson, Map.class);
newMap = JsonUtils.parseMap(configJson);
}
} catch (Exception e) {
throw new RenException(ErrorCode.PARAM_JSON_INVALID);
@@ -298,8 +298,8 @@ public class SysParamsServiceImpl extends BaseServiceImpl<SysParamsDao, SysParam
// 检查addressBook功能是否被关闭
if (currentMap != null && newMap != null) {
Map<String, Object> currentFeatures = (Map<String, Object>) currentMap.get("features");
Map<String, Object> newFeatures = (Map<String, Object>) newMap.get("features");
Map<?, ?> currentFeatures = Map.class.cast(currentMap.get("features"));
Map<?, ?> newFeatures = Map.class.cast(newMap.get("features"));
if (currentFeatures != null && newFeatures != null) {
Object currentAddressBookObj = currentFeatures.get("addressBook");
@@ -308,16 +308,14 @@ public class SysParamsServiceImpl extends BaseServiceImpl<SysParamsDao, SysParam
Boolean currentEnabled = false;
Boolean newEnabled = false;
if (currentAddressBookObj instanceof Map) {
Map<String, Object> currentAddressBook = (Map<String, Object>) currentAddressBookObj;
currentEnabled = currentAddressBook.get("enabled") != null
? (Boolean) currentAddressBook.get("enabled") : false;
if (currentAddressBookObj instanceof Map<?, ?> currentAddressBook) {
Object enabled = currentAddressBook.get("enabled");
currentEnabled = enabled != null ? Boolean.class.cast(enabled) : false;
}
if (newAddressBookObj instanceof Map) {
Map<String, Object> newAddressBook = (Map<String, Object>) newAddressBookObj;
newEnabled = newAddressBook.get("enabled") != null
? (Boolean) newAddressBook.get("enabled") : false;
if (newAddressBookObj instanceof Map<?, ?> newAddressBook) {
Object enabled = newAddressBook.get("enabled");
newEnabled = enabled != null ? Boolean.class.cast(enabled) : false;
}
// 如果之前是启用状态,现在被禁用,删除所有call_device插件
@@ -117,7 +117,7 @@ public class TimbreServiceImpl extends BaseServiceImpl<TimbreDao, TimbreEntity>
@Override
@Transactional(rollbackFor = Exception.class)
public void delete(String[] ids) {
baseDao.deleteBatchIds(Arrays.asList(ids));
baseDao.deleteByIds(Arrays.asList(ids));
}
@Override
@@ -127,7 +127,7 @@ public class VoiceCloneServiceImpl extends BaseServiceImpl<VoiceCloneDao, VoiceC
@Override
public void delete(String[] ids) {
baseDao.deleteBatchIds(Arrays.asList(ids));
baseDao.deleteByIds(Arrays.asList(ids));
}
@Override
@@ -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;
}
}
@@ -57,7 +57,8 @@ class BaseServiceImplTest {
when(sqlSessionFactory.openSession(ExecutorType.BATCH)).thenReturn(sqlSession);
when(sqlSession.insert(eq(INSERT_STATEMENT), any(TestEntity.class))).thenReturn(1);
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 second = new TestEntity(2L);
@@ -126,6 +127,19 @@ class BaseServiceImplTest {
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
void activeTransactionSynchronizationUsesTransactionAwareCommitAndLifecycle() {
SqlSessionFactory sqlSessionFactory = mock(SqlSessionFactory.class);
@@ -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));
}
}
@@ -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));
}
}
@@ -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);
}
}
@@ -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);
}
}