Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,21 @@ public class AgentController {
private final RedisUtils redisUtils;

@GetMapping("/list")
@Operation(summary = "Get user agents list")
@Operation(summary = "Get agents list (admin gets all agents, user gets own agents)")
@RequiresPermissions("sys:role:normal")
public Result<List<AgentDTO>> getUserAgents() {
public Result<List<AgentDTO>> getAgentsList() {
UserDetail user = SecurityUser.getUser();
List<AgentDTO> agents = agentService.getUserAgents(user.getId());
List<AgentDTO> agents;

// Check if user is super admin
if (user.getSuperAdmin() != null && user.getSuperAdmin() == 1) {
// Admin sees all agents from all users with owner information
agents = agentService.getAllAgentsForAdmin();
} else {
// Regular user sees only their own agents
agents = agentService.getUserAgents(user.getId());
}

return new Result<List<AgentDTO>>().ok(agents);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package xiaozhi.modules.agent.dao;

import java.util.List;
import java.util.Map;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import org.apache.ibatis.annotations.Select;
import xiaozhi.common.dao.BaseDao;
import xiaozhi.modules.agent.entity.AgentEntity;
Expand Down Expand Up @@ -30,6 +32,24 @@ public interface AgentDao extends BaseDao<AgentEntity> {
" ORDER BY d.id DESC LIMIT 1")
AgentEntity getDefaultAgentByMacAddress(@Param("macAddress") String macAddress);

/**
* 获取所有智能体及其所有者信息(管理员专用)
*
* @return 所有智能体列表及用户信息
*/
@Select("SELECT " +
"a.id, a.agent_name, a.system_prompt, a.tts_model_id, " +
"a.llm_model_id, a.vllm_model_id, a.mem_model_id, a.tts_voice_id, " +
"a.created_at, a.updated_at, a.user_id, " +
"u.username as owner_username, " +
"GROUP_CONCAT(d.mac_address SEPARATOR ',') as device_mac_addresses " +
"FROM ai_agent a " +
"LEFT JOIN sys_user u ON a.user_id = u.id " +
"LEFT JOIN ai_device d ON a.id = d.agent_id " +
"GROUP BY a.id " +
"ORDER BY a.created_at DESC")
List<Map<String, Object>> getAllAgentsWithOwnerInfo();

/**
* 根据id查询agent信息,包括插件信息
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,14 @@ public class AgentDTO {

@Schema(description = "设备数量", example = "10")
private Integer deviceCount;

@Schema(description = "设备MAC地址列表", example = "AA:BB:CC:DD:EE:FF,11:22:33:44:55:66")
private String deviceMacAddresses;

// 管理员专用字段 - 智能体所有者信息
@Schema(description = "所有者用户名", example = "john_doe")
private String ownerUsername;

@Schema(description = "创建时间", example = "2024-03-20 10:00:00")
private Date createDate;
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,21 @@ public interface AgentService extends BaseService<AgentEntity> {
*/
boolean insert(AgentEntity entity);

/**
* 获取用户的智能体列表
*
* @param userId 用户ID
* @return 智能体列表
*/
List<AgentDTO> getUserAgents(Long userId);

/**
* 获取所有智能体列表(管理员专用,包含用户信息)
*
* @return 所有智能体列表
*/
List<AgentDTO> getAllAgentsForAdmin();

/**
* 根据用户ID删除智能体
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,59 @@ public List<AgentDTO> getUserAgents(Long userId) {
}).collect(Collectors.toList());
}

@Override
public List<AgentDTO> getAllAgentsForAdmin() {
List<Map<String, Object>> agentMaps = agentDao.getAllAgentsWithOwnerInfo();
return agentMaps.stream().map(agentMap -> {
AgentDTO dto = new AgentDTO();

// 基础智能体信息
String agentId = (String) agentMap.get("id");
dto.setId(agentId);
dto.setAgentName((String) agentMap.get("agent_name"));
dto.setSystemPrompt((String) agentMap.get("system_prompt"));

// Handle LocalDateTime to Date conversion for createDate
Object createdAt = agentMap.get("created_at");
if (createdAt instanceof java.time.LocalDateTime) {
dto.setCreateDate(java.sql.Timestamp.valueOf((java.time.LocalDateTime) createdAt));
}

// 获取模型名称 - 同用户方法
String ttsModelId = (String) agentMap.get("tts_model_id");
String llmModelId = (String) agentMap.get("llm_model_id");
String vllmModelId = (String) agentMap.get("vllm_model_id");
String memModelId = (String) agentMap.get("mem_model_id");
String ttsVoiceId = (String) agentMap.get("tts_voice_id");

dto.setTtsModelName(modelConfigService.getModelNameById(ttsModelId));
dto.setLlmModelName(modelConfigService.getModelNameById(llmModelId));
dto.setVllmModelName(modelConfigService.getModelNameById(vllmModelId));
dto.setMemModelId(memModelId);
dto.setTtsVoiceName(timbreModelService.getTimbreNameById(ttsVoiceId));

// 获取智能体最近的最后连接时长 - 同用户方法
dto.setLastConnectedAt(deviceService.getLatestLastConnectionTime(agentId));

// 获取设备MAC地址列表 - 管理员专用
String macAddresses = (String) agentMap.get("device_mac_addresses");
dto.setDeviceMacAddresses(macAddresses);

// 计算设备数量(从MAC地址列表或使用原方法)
if (macAddresses != null && !macAddresses.isEmpty()) {
dto.setDeviceCount(macAddresses.split(",").length);
} else {
// 使用原来的方法获取设备数量
dto.setDeviceCount(getDeviceCountByAgentId(agentId));
}

// 管理员专用字段 - 用户信息
dto.setOwnerUsername((String) agentMap.get("owner_username"));

return dto;
}).collect(Collectors.toList());
}

@Override
public Integer getDeviceCountByAgentId(String agentId) {
if (StringUtils.isBlank(agentId)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,17 @@ public Result<String> registerDevice(@RequestBody DeviceRegisterDTO deviceRegist
@RequiresPermissions("sys:role:normal")
public Result<List<DeviceEntity>> getUserDevices(@PathVariable String agentId) {
UserDetail user = SecurityUser.getUser();
List<DeviceEntity> devices = deviceService.getUserDevices(user.getId(), agentId);
List<DeviceEntity> devices;

// Check if user is super admin
if (user.getSuperAdmin() != null && user.getSuperAdmin() == 1) {
// Admin can see all devices for any agent
devices = deviceService.getDevicesByAgentId(agentId);
} else {
// Regular user can only see their own devices
devices = deviceService.getUserDevices(user.getId(), agentId);
}

return new Result<List<DeviceEntity>>().ok(devices);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ DeviceReportRespDTO checkDeviceActive(String macAddress, String clientId,
*/
List<DeviceEntity> getUserDevices(Long userId, String agentId);

/**
* 获取指定智能体的所有设备列表(管理员专用)
*/
List<DeviceEntity> getDevicesByAgentId(String agentId);

/**
* 解绑设备
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,13 @@ public List<DeviceEntity> getUserDevices(Long userId, String agentId) {
return baseDao.selectList(wrapper);
}

@Override
public List<DeviceEntity> getDevicesByAgentId(String agentId) {
QueryWrapper<DeviceEntity> wrapper = new QueryWrapper<>();
wrapper.eq("agent_id", agentId);
return baseDao.selectList(wrapper);
}

@Override
public void unbindDevice(Long userId, String deviceId) {
UpdateWrapper<DeviceEntity> wrapper = new UpdateWrapper<>();
Expand Down
2 changes: 1 addition & 1 deletion main/manager-api/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ spring:
# Profile will be set by SPRING_PROFILES_ACTIVE environment variable
# Default to 'dev' if not specified
profiles:
default: prod
default: dev
messages:
encoding: UTF-8
basename: i18n/messages
Expand Down
21 changes: 19 additions & 2 deletions main/manager-web/src/components/DeviceItem.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
<template>
<div class="device-item">
<div style="display: flex;justify-content: space-between;">
<div style="font-weight: 700;font-size: 18px;text-align: left;color: #3d4566;">
{{ device.agentName }}
<div>
<div style="font-weight: 700;font-size: 18px;text-align: left;color: #3d4566;">
{{ device.agentName }}
</div>
<!-- Admin-only: Show owner username -->
<div v-if="isAdmin && device.ownerUsername" class="owner-info">
Owner: {{ device.ownerUsername }}
</div>
</div>
<div>
<img src="@/assets/home/delete.png" alt="" class="action-icon delete-icon"
Expand Down Expand Up @@ -53,6 +59,9 @@ export default {
return { switchValue: false }
},
computed: {
isAdmin() {
return this.$store.getters.getIsSuperAdmin;
},
formattedLastConnectedTime() {
if (!this.device.lastConnectedAt) return 'No conversations yet';

Expand Down Expand Up @@ -112,6 +121,14 @@ export default {
text-align: left;
}

.owner-info {
font-weight: 400;
font-size: 12px;
color: #5778ff;
text-align: left;
margin-top: 5px;
}

.settings-btn {
font-weight: 500;
font-size: 11px;
Expand Down