Merge pull request #2794 from xinnan-tech/20260104SSL

拓展首页的搜索栏-支持同时搜索设备mac和智能体名字
This commit is contained in:
hrz
2026-01-05 16:54:20 +08:00
committed by GitHub
14 changed files with 140 additions and 61 deletions
@@ -73,9 +73,13 @@ public class AgentController {
@GetMapping("/list")
@Operation(summary = "获取用户智能体列表")
@RequiresPermissions("sys:role:normal")
public Result<List<AgentDTO>> getUserAgents() {
public Result<List<AgentDTO>> getUserAgents(
@RequestParam(value = "keyword", required = false) String keyword,
@RequestParam(value = "searchType", defaultValue = "name") String searchType) {
UserDetail user = SecurityUser.getUser();
List<AgentDTO> agents = agentService.getUserAgents(user.getId());
// 直接调用整合后的getUserAgents方法,无需再区分搜索和普通查询
List<AgentDTO> agents = agentService.getUserAgents(user.getId(), keyword, searchType);
return new Result<List<AgentDTO>>().ok(agents);
}
@@ -271,4 +275,6 @@ public class AgentController {
.body(audioData);
}
}
@@ -1,6 +1,7 @@
package xiaozhi.modules.agent.dto;
import java.util.Date;
import java.util.List;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@@ -54,9 +54,11 @@ public interface AgentService extends BaseService<AgentEntity> {
* 获取用户智能体列表
*
* @param userId 用户ID
* @param keyword 搜索关键词
* @param searchType 搜索类型(name - 按名称搜索,mac - 按MAC地址搜索)
* @return 智能体列表
*/
List<AgentDTO> getUserAgents(Long userId);
List<AgentDTO> getUserAgents(Long userId, String keyword, String searchType);
/**
* 根据智能体ID获取设备数量
@@ -98,4 +100,6 @@ public interface AgentService extends BaseService<AgentEntity> {
* @return 创建的智能体ID
*/
String createAgent(AgentCreateDTO dto);
}
@@ -42,6 +42,7 @@ import xiaozhi.modules.agent.service.AgentPluginMappingService;
import xiaozhi.modules.agent.service.AgentService;
import xiaozhi.modules.agent.service.AgentTemplateService;
import xiaozhi.modules.agent.vo.AgentInfoVO;
import xiaozhi.modules.device.entity.DeviceEntity;
import xiaozhi.modules.device.service.DeviceService;
import xiaozhi.modules.model.dto.ModelProviderDTO;
import xiaozhi.modules.model.dto.VoiceDTO;
@@ -88,13 +89,13 @@ public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> imp
agent.setChatHistoryConf(Constant.ChatHistoryConfEnum.RECORD_TEXT_AUDIO.getCode());
}
}
// 查询上下文源配置
AgentContextProviderEntity contextProviderEntity = agentContextProviderService.getByAgentId(id);
if (contextProviderEntity != null) {
agent.setContextProviders(contextProviderEntity.getContextProviders());
}
// 无需额外查询插件列表,已通过SQL查询出来
return agent;
}
@@ -127,38 +128,75 @@ public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> imp
}
@Override
public List<AgentDTO> getUserAgents(Long userId) {
QueryWrapper<AgentEntity> wrapper = new QueryWrapper<>();
wrapper.eq("user_id", userId);
List<AgentEntity> agents = agentDao.selectList(wrapper);
return agents.stream().map(agent -> {
AgentDTO dto = new AgentDTO();
dto.setId(agent.getId());
dto.setAgentName(agent.getAgentName());
dto.setSystemPrompt(agent.getSystemPrompt());
public List<AgentDTO> getUserAgents(Long userId, String keyword, String searchType) {
QueryWrapper<AgentEntity> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("user_id", userId);
// 获取 TTS 模型名称
dto.setTtsModelName(modelConfigService.getModelNameById(agent.getTtsModelId()));
// 如果有搜索关键词,根据搜索类型添加相应的查询条件
if (StringUtils.isNotBlank(keyword)) {
if ("mac".equals(searchType)) {
// 按MAC地址搜索:先搜索设备,再获取对应的智能体
List<DeviceEntity> devices = deviceService.searchDevicesByMacAddress(keyword, userId);
// 获取 LLM 模型名称
dto.setLlmModelName(modelConfigService.getModelNameById(agent.getLlmModelId()));
if (!devices.isEmpty()) {
// 获取设备对应的智能体ID列表
List<String> agentIds = devices.stream()
.map(DeviceEntity::getAgentId)
.distinct()
.collect(Collectors.toList());
// 获取 VLLM 模型名称
dto.setVllmModelName(modelConfigService.getModelNameById(agent.getVllmModelId()));
if (!agentIds.isEmpty()) {
queryWrapper.in("id", agentIds);
} else {
return new ArrayList<>();
}
} else {
return new ArrayList<>();
}
} else {
// 按名称搜索
queryWrapper.like("agent_name", keyword);
}
}
// 获取记忆模型名称
dto.setMemModelId(agent.getMemModelId());
// 执行查询
List<AgentEntity> agentEntities = baseDao.selectList(queryWrapper);
// 获取 TTS 音色名称
dto.setTtsVoiceName(timbreModelService.getTimbreNameById(agent.getTtsVoiceId()));
// 转换为DTO并设置所有必要字段
return agentEntities.stream().map(this::buildAgentDTO).collect(Collectors.toList());
}
// 获取智能体最近的最后连接时长
dto.setLastConnectedAt(deviceService.getLatestLastConnectionTime(agent.getId()));
/**
* 将AgentEntity转换为AgentDTO
*/
private AgentDTO buildAgentDTO(AgentEntity agent) {
AgentDTO dto = new AgentDTO();
dto.setId(agent.getId());
dto.setAgentName(agent.getAgentName());
dto.setSystemPrompt(agent.getSystemPrompt());
// 获取设备数量
dto.setDeviceCount(getDeviceCountByAgentId(agent.getId()));
return dto;
}).collect(Collectors.toList());
// 获取 TTS 模型名称
dto.setTtsModelName(modelConfigService.getModelNameById(agent.getTtsModelId()));
// 获取 LLM 模型名称
dto.setLlmModelName(modelConfigService.getModelNameById(agent.getLlmModelId()));
// 获取 VLLM 模型名称
dto.setVllmModelName(modelConfigService.getModelNameById(agent.getVllmModelId()));
// 获取记忆模型名称
dto.setMemModelId(agent.getMemModelId());
// 获取 TTS 音色名称
dto.setTtsVoiceName(timbreModelService.getTimbreNameById(agent.getTtsVoiceId()));
// 获取智能体最近的最后连接时长
dto.setLastConnectedAt(deviceService.getLatestLastConnectionTime(agent.getId()));
// 获取设备数量
dto.setDeviceCount(getDeviceCountByAgentId(agent.getId()));
return dto;
}
@Override
@@ -468,4 +506,5 @@ public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> imp
agentPluginMappingService.saveBatch(toInsert);
return entity.getId();
}
}
}
@@ -108,4 +108,13 @@ public interface DeviceService extends BaseService<DeviceEntity> {
*/
String generateWebSocketToken(String clientId, String username) throws Exception;
/**
* 根据MAC地址搜索设备
*
* @param macAddress MAC地址关键词
* @param userId 用户ID
* @return 设备列表
*/
List<DeviceEntity> searchDevicesByMacAddress(String macAddress, Long userId);
}
@@ -496,6 +496,14 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
redisUtils.delete(RedisKeys.getAgentDeviceCountById(dto.getAgentId()));
}
@Override
public List<DeviceEntity> searchDevicesByMacAddress(String macAddress, Long userId) {
QueryWrapper<DeviceEntity> wrapper = new QueryWrapper<>();
wrapper.like("mac_address", macAddress);
wrapper.eq("user_id", userId);
return deviceDao.selectList(wrapper);
}
/**
* 生成MQTT密码签名
*
+16
View File
@@ -382,4 +382,20 @@ export default {
});
}).send();
},
// 搜索智能体
searchAgent(keyword, searchType, callback) {
RequestService.sendRequest()
.url(`${getServiceUrl()}/agent/list?keyword=${encodeURIComponent(keyword)}&searchType=${searchType}`)
.method('GET')
.success((res) => {
RequestService.clearRequestTime();
callback(res);
})
.networkFail(() => {
RequestService.reAjaxFun(() => {
this.searchAgent(keyword, searchType, callback);
});
}).send();
},
}
+2 -12
View File
@@ -408,18 +408,8 @@ export default {
// 保存搜索历史
this.saveSearchHistory(searchValue);
try {
// 创建不区分大小写的正则表达式
const regex = new RegExp(searchValue, "i");
// 触发搜索事件,将正则表达式传递给父组件
this.$emit("search", regex);
} catch (error) {
console.error("正则表达式创建失败:", error);
this.$message.error({
message: this.$t("message.error"),
showClose: true,
});
}
// 触发搜索事件,将搜索关键词传递给父组件
this.$emit("search", searchValue);
// 搜索完成后让输入框失去焦点,从而触发blur事件隐藏搜索历史
if (this.$refs.searchInput) {
+1 -1
View File
@@ -25,7 +25,7 @@ export default {
'header.featureManagement': 'Systemfunktionsverwaltung',
'header.changePassword': 'Passwort ändern',
'header.logout': 'Abmelden',
'header.searchPlaceholder': 'Nach Namen suchen..',
'header.searchPlaceholder': 'Namen oder MAC suchen',
// McpToolCallDialog component text
'mcpToolCall.title': 'Werkzeugaufruf',
+1 -1
View File
@@ -25,7 +25,7 @@ export default {
'header.featureManagement': 'System Feature Management',
'header.changePassword': 'Change Password',
'header.logout': 'Logout',
'header.searchPlaceholder': 'Search by name..',
'header.searchPlaceholder': 'Search name or mac',
// McpToolCallDialog component text
'mcpToolCall.title': 'Tool Call',
+1 -1
View File
@@ -25,7 +25,7 @@ export default {
'header.featureManagement': 'Cấu hình chức năng hệ thống',
'header.changePassword': 'Đổi mật khẩu',
'header.logout': 'Đăng xuất',
'header.searchPlaceholder': 'Tìm kiếm theo tên..',
'header.searchPlaceholder': 'Tìm tên hoặc MAC',
// McpToolCallDialog component text
'mcpToolCall.title': 'Gọi công cụ',
+1 -1
View File
@@ -25,7 +25,7 @@ export default {
'header.featureManagement': '系统功能配置',
'header.changePassword': '修改密码',
'header.logout': '退出登录',
'header.searchPlaceholder': '输入名称搜索..',
'header.searchPlaceholder': '输入名称或mac搜索',
// McpToolCallDialog组件文本
'mcpToolCall.title': '工具调用',
+1 -1
View File
@@ -25,7 +25,7 @@ export default {
'header.featureManagement': '系統功能配置',
'header.changePassword': '修改密碼',
'header.logout': '退出登錄',
'header.searchPlaceholder': '輸入名稱搜索..',
'header.searchPlaceholder': '輸入名稱或mac搜索',
// McpToolCallDialog组件文本
'mcpToolCall.title': '工具調用',
+19 -13
View File
@@ -119,26 +119,32 @@ export default {
handleDeviceManage() {
this.$router.push('/device-management');
},
handleSearch(regex) {
handleSearch(keyword) {
this.isSearching = true;
this.searchRegex = regex;
this.applySearchFilter();
this.isLoading = true;
// 检测MAC地址格式:包含4个冒号
const isMac = /^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$/.test(keyword)
const searchType = isMac ? 'mac' : 'name';
Api.agent.searchAgent(keyword, searchType, ({ data }) => {
if (data?.data) {
this.devices = data.data.map(item => ({
...item,
agentId: item.id
}));
}
this.isLoading = false;
}, (error) => {
console.error('搜索智能体失败:', error);
this.isLoading = false;
this.$message.error(this.$t('message.searchFailed'));
});
},
handleSearchReset() {
this.isSearching = false;
this.searchRegex = null;
// 直接将原始设备列表赋值给显示设备列表,避免重新加载数据
this.devices = [...this.originalDevices];
},
applySearchFilter() {
if (!this.isSearching || !this.searchRegex) {
this.devices = [...this.originalDevices];
return;
}
this.devices = this.originalDevices.filter(device => {
return this.searchRegex.test(device.agentName);
});
},
// 搜索更新智能体列表
handleSearchResult(filteredList) {
this.devices = filteredList; // 更新设备列表