add online monitor and fix admin scopes

This commit is contained in:
baiyanyun
2026-06-02 20:50:42 +08:00
parent f69fb46c59
commit a01fc93594
17 changed files with 998 additions and 7 deletions

View File

@@ -0,0 +1,92 @@
package com.xspaceagi.sandbox.application.dto;
import com.xspaceagi.system.infra.dao.entity.User;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.Date;
import java.util.List;
@Schema(description = "在线监控聚合信息")
@Data
public class OnlineMonitorDto {
@Schema(description = "客户端总数")
private long totalClientCount;
@Schema(description = "在线客户端数")
private long onlineClientCount;
@Schema(description = "离线客户端数")
private long offlineClientCount;
@Schema(description = "已停用客户端数")
private long inactiveClientCount;
@Schema(description = "用户总数")
private long totalUserCount;
@Schema(description = "启用用户数")
private long enabledUserCount;
@Schema(description = "禁用用户数")
private long disabledUserCount;
@Schema(description = "用户组监控信息")
private List<GroupMonitorDto> groups;
@Schema(description = "用户监控信息")
private List<UserMonitorDto> users;
@Schema(description = "客户端监控信息")
private List<ClientMonitorDto> clients;
@Schema(description = "用户组监控信息")
@Data
public static class GroupMonitorDto {
private Long groupId;
private String groupName;
private Integer authEnabled;
private Integer status;
private Date expireTime;
private Integer maxUserCount;
private long currentUserCount;
private Integer maxClientCount;
private long currentClientCount;
private long onlineClientCount;
private long offlineClientCount;
private long inactiveClientCount;
}
@Schema(description = "用户监控信息")
@Data
public static class UserMonitorDto {
private Long userId;
private String userName;
private String nickName;
private User.Role role;
private User.Status status;
private List<String> groupNames;
private long clientCount;
private long onlineClientCount;
private long inactiveClientCount;
private Date lastLoginTime;
}
@Schema(description = "客户端监控信息")
@Data
public static class ClientMonitorDto {
private Long id;
private String name;
private Long userId;
private String userName;
private String nickName;
private Long groupId;
private String groupName;
private Boolean isActive;
private boolean online;
private Integer maxAgentCount;
private Date created;
private Date modified;
}
}

View File

@@ -1,18 +1,23 @@
package com.xspaceagi.sandbox.ui.web.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xspaceagi.sandbox.application.dto.OnlineMonitorDto;
import com.xspaceagi.sandbox.application.dto.SandboxConfigDto;
import com.xspaceagi.sandbox.application.dto.SandboxConfigQueryDto;
import com.xspaceagi.sandbox.application.dto.SandboxGlobalConfigDto;
import com.xspaceagi.sandbox.application.service.SandboxConfigApplicationService;
import com.xspaceagi.system.application.dto.UserDto;
import com.xspaceagi.system.application.service.SysGroupApplicationService;
import com.xspaceagi.system.application.service.SysRoleApplicationService;
import com.xspaceagi.system.infra.dao.entity.SysGroup;
import com.xspaceagi.system.infra.dao.entity.User;
import com.xspaceagi.system.infra.dao.service.UserService;
import com.xspaceagi.system.spec.annotation.RequireResource;
import com.xspaceagi.system.spec.common.RequestContext;
import com.xspaceagi.system.spec.dto.ReqResult;
import com.xspaceagi.system.spec.enums.ErrorCodeEnum;
import com.xspaceagi.system.spec.enums.GroupEnum;
import com.xspaceagi.system.spec.enums.RoleEnum;
import com.xspaceagi.system.spec.exception.BizException;
import com.xspaceagi.system.spec.exception.BizExceptionCodeEnum;
import io.swagger.v3.oas.annotations.Operation;
@@ -21,7 +26,12 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
@@ -41,6 +51,12 @@ public class SandboxConfigManageController {
@Resource
private SysGroupApplicationService sysGroupApplicationService;
@Resource
private SysRoleApplicationService sysRoleApplicationService;
@Resource
private UserService userService;
@RequireResource(SANDBOX_CONFIG_QUERY)
@Operation(summary = "分页查询配置列表")
@PostMapping("/page")
@@ -148,6 +164,154 @@ public class SandboxConfigManageController {
return ReqResult.success(globalConfig);
}
@RequireResource(ONLINE_MONITOR_QUERY)
@Operation(summary = "查询在线监控信息")
@GetMapping("/online-monitor")
public ReqResult<OnlineMonitorDto> onlineMonitor() {
boolean platformAdmin = isPlatformAdmin();
List<SysGroup> groups = visibleGroups();
Set<Long> groupIds = groups.stream().map(SysGroup::getId).collect(Collectors.toSet());
List<User> users = visibleUsers(platformAdmin, groupIds);
Set<Long> userIds = users.stream().map(User::getId).collect(Collectors.toSet());
SandboxConfigQueryDto queryDto = new SandboxConfigQueryDto();
queryDto.setPageNum(1);
queryDto.setPageSize(100000);
if (!platformAdmin && !groupIds.isEmpty()) {
queryDto.setGroupIds(new ArrayList<>(groupIds));
} else if (!platformAdmin) {
queryDto.setGroupIds(List.of(-1L));
}
List<SandboxConfigDto> configs = sandboxConfigApplicationService.pageQuery(queryDto).getRecords().stream()
.filter(config -> config.getUserId() != null)
.filter(config -> platformAdmin || userIds.contains(config.getUserId()))
.toList();
return ReqResult.success(buildOnlineMonitor(groups, users, configs));
}
private OnlineMonitorDto buildOnlineMonitor(List<SysGroup> groups, List<User> users, List<SandboxConfigDto> configs) {
Map<Long, SysGroup> groupMap = groups.stream()
.filter(group -> group.getId() != null)
.collect(Collectors.toMap(SysGroup::getId, group -> group, (a, b) -> a));
Map<Long, User> userMap = users.stream()
.filter(user -> user.getId() != null)
.collect(Collectors.toMap(User::getId, user -> user, (a, b) -> a));
Map<Long, List<SysGroup>> userGroupMap = new HashMap<>();
groups.forEach(group -> sysGroupApplicationService.getUserListByGroupId(group.getId())
.forEach(user -> userGroupMap.computeIfAbsent(user.getId(), key -> new ArrayList<>()).add(group)));
OnlineMonitorDto dto = new OnlineMonitorDto();
dto.setTotalClientCount(configs.size());
dto.setOnlineClientCount(configs.stream().filter(config -> Boolean.TRUE.equals(config.getIsActive()) && config.isOnline()).count());
dto.setInactiveClientCount(configs.stream().filter(config -> !Boolean.TRUE.equals(config.getIsActive())).count());
dto.setOfflineClientCount(configs.stream().filter(config -> Boolean.TRUE.equals(config.getIsActive()) && !config.isOnline()).count());
dto.setTotalUserCount(users.size());
dto.setEnabledUserCount(users.stream().filter(user -> user.getStatus() == User.Status.Enabled).count());
dto.setDisabledUserCount(users.stream().filter(user -> user.getStatus() == User.Status.Disabled).count());
dto.setGroups(groups.stream()
.map(group -> buildGroupMonitor(group, users, configs))
.sorted(Comparator.comparing(OnlineMonitorDto.GroupMonitorDto::getGroupId, Comparator.nullsLast(Long::compareTo)))
.toList());
dto.setUsers(users.stream()
.map(user -> buildUserMonitor(user, userGroupMap, configs))
.sorted(Comparator.comparing(OnlineMonitorDto.UserMonitorDto::getUserId, Comparator.nullsLast(Long::compareTo)))
.toList());
dto.setClients(configs.stream()
.map(config -> buildClientMonitor(config, userMap, groupMap))
.sorted(Comparator.comparing(OnlineMonitorDto.ClientMonitorDto::getId, Comparator.nullsLast(Long::compareTo)))
.toList());
return dto;
}
private OnlineMonitorDto.GroupMonitorDto buildGroupMonitor(SysGroup group, List<User> users, List<SandboxConfigDto> configs) {
Set<Long> groupUserIds = sysGroupApplicationService.getUserListByGroupId(group.getId()).stream()
.map(User::getId)
.collect(Collectors.toSet());
List<SandboxConfigDto> groupConfigs = configs.stream()
.filter(config -> Objects.equals(config.getGroupId(), group.getId()))
.toList();
OnlineMonitorDto.GroupMonitorDto dto = new OnlineMonitorDto.GroupMonitorDto();
dto.setGroupId(group.getId());
dto.setGroupName(group.getName());
dto.setAuthEnabled(group.getAuthEnabled());
dto.setStatus(group.getStatus());
dto.setExpireTime(group.getExpireTime());
dto.setMaxUserCount(group.getMaxUserCount());
dto.setCurrentUserCount(users.stream().filter(user -> groupUserIds.contains(user.getId())).count());
dto.setMaxClientCount(group.getMaxClientCount());
dto.setCurrentClientCount(groupConfigs.size());
dto.setOnlineClientCount(groupConfigs.stream().filter(config -> Boolean.TRUE.equals(config.getIsActive()) && config.isOnline()).count());
dto.setInactiveClientCount(groupConfigs.stream().filter(config -> !Boolean.TRUE.equals(config.getIsActive())).count());
dto.setOfflineClientCount(groupConfigs.stream().filter(config -> Boolean.TRUE.equals(config.getIsActive()) && !config.isOnline()).count());
return dto;
}
private OnlineMonitorDto.UserMonitorDto buildUserMonitor(User user, Map<Long, List<SysGroup>> userGroupMap, List<SandboxConfigDto> configs) {
List<SandboxConfigDto> userConfigs = configs.stream()
.filter(config -> Objects.equals(config.getUserId(), user.getId()))
.toList();
OnlineMonitorDto.UserMonitorDto dto = new OnlineMonitorDto.UserMonitorDto();
dto.setUserId(user.getId());
dto.setUserName(user.getUserName());
dto.setNickName(user.getNickName());
dto.setRole(user.getRole());
dto.setStatus(user.getStatus());
dto.setGroupNames(userGroupMap.getOrDefault(user.getId(), List.of()).stream().map(SysGroup::getName).toList());
dto.setClientCount(userConfigs.size());
dto.setOnlineClientCount(userConfigs.stream().filter(config -> Boolean.TRUE.equals(config.getIsActive()) && config.isOnline()).count());
dto.setInactiveClientCount(userConfigs.stream().filter(config -> !Boolean.TRUE.equals(config.getIsActive())).count());
dto.setLastLoginTime(user.getLastLoginTime());
return dto;
}
private OnlineMonitorDto.ClientMonitorDto buildClientMonitor(SandboxConfigDto config, Map<Long, User> userMap, Map<Long, SysGroup> groupMap) {
User user = userMap.get(config.getUserId());
SysGroup group = groupMap.get(config.getGroupId());
OnlineMonitorDto.ClientMonitorDto dto = new OnlineMonitorDto.ClientMonitorDto();
dto.setId(config.getId());
dto.setName(config.getName());
dto.setUserId(config.getUserId());
dto.setUserName(user == null ? null : user.getUserName());
dto.setNickName(user == null ? null : user.getNickName());
dto.setGroupId(config.getGroupId());
dto.setGroupName(group == null ? null : group.getName());
dto.setIsActive(config.getIsActive());
dto.setOnline(config.isOnline());
dto.setMaxAgentCount(config.getMaxAgentCount());
dto.setCreated(config.getCreated());
dto.setModified(config.getModified());
return dto;
}
private List<SysGroup> visibleGroups() {
if (isPlatformAdmin()) {
return sysGroupApplicationService.getGroupList(new SysGroup()).stream()
.filter(group -> !GroupEnum.DEFAULT_GROUP.getCode().equals(group.getCode()))
.toList();
}
Set<Long> groupIds = currentUserGroupIds();
if (groupIds.isEmpty()) {
return List.of();
}
return sysGroupApplicationService.listGroupsByIds(new ArrayList<>(groupIds));
}
private List<User> visibleUsers(boolean platformAdmin, Set<Long> groupIds) {
if (platformAdmin) {
return userService.list().stream()
.filter(user -> user.getStatus() != User.Status.Deleted)
.toList();
}
return groupIds.stream()
.flatMap(groupId -> sysGroupApplicationService.getUserListByGroupId(groupId).stream())
.filter(user -> user.getStatus() != User.Status.Deleted)
.collect(Collectors.toMap(User::getId, user -> user, (a, b) -> a))
.values().stream().toList();
}
private void applySandboxConfigManageScope(SandboxConfigQueryDto queryDto) {
if (queryDto == null || isPlatformAdmin()) {
return;
@@ -172,13 +336,18 @@ public class SandboxConfigManageController {
}
private Set<Long> currentUserGroupIds() {
return sysGroupApplicationService.getEffectiveGroupListByUserId(getCurrentUserDto().getId()).stream()
return sysGroupApplicationService.getGroupListByUserId(getCurrentUserDto().getId()).stream()
.filter(group -> !GroupEnum.DEFAULT_GROUP.getCode().equals(group.getCode()))
.map(SysGroup::getId)
.collect(Collectors.toSet());
}
private boolean isPlatformAdmin() {
return getCurrentUserDto().getRole() == User.Role.Admin;
UserDto currentUser = getCurrentUserDto();
return currentUser.getRole() == User.Role.Admin
&& (currentUserGroupIds().isEmpty()
|| sysRoleApplicationService.getRoleListByUserId(currentUser.getId()).stream()
.anyMatch(role -> RoleEnum.SUPER_ADMIN.getCode().equals(role.getCode())));
}
private UserDto getCurrentUserDto() {

View File

@@ -145,13 +145,22 @@
"sortIndex": 16,
"status": 1
},
{
"code": "online_monitor",
"name": "在线监控模块",
"description": "在线监控模块",
"source": 1,
"type": 1,
"sortIndex": 17,
"status": 1
},
{
"code": "category_config",
"name": "分类管理模块",
"description": "分类管理模块",
"source": 1,
"type": 1,
"sortIndex": 17,
"sortIndex": 18,
"status": 1
},
{
@@ -924,6 +933,16 @@
"sortIndex": 1,
"status": 1
},
{
"code": "online_monitor_query",
"name": "查询",
"description": "查询在线监控",
"source": 1,
"type": 2,
"parentCode": "online_monitor",
"sortIndex": 1,
"status": 1
},
{
"code": "subscription_points_query",
"name": "查询",
@@ -2645,6 +2664,18 @@
"sortIndex": 10,
"status": 1
},
{
"parentCode": "system_manage",
"code": "online_monitor",
"name": "在线监控",
"description": "在线监控",
"source": 1,
"path": "/system/online-monitor",
"openType": 1,
"icon": "",
"sortIndex": 11,
"status": 1
},
{
"parentCode": "workspace",
"code": "member_setting",
@@ -2664,7 +2695,7 @@
"source": 1,
"openType": 1,
"icon": "",
"sortIndex": 11,
"sortIndex": 12,
"status": 1
},
{
@@ -2675,7 +2706,7 @@
"source": 1,
"openType": 1,
"icon": "",
"sortIndex": 12,
"sortIndex": 13,
"status": 1
},
{
@@ -3203,6 +3234,11 @@
"resourceCode": "sandbox_config",
"resourceBindType": 1
},
{
"menuCode": "online_monitor",
"resourceCode": "online_monitor",
"resourceBindType": 1
},
{
"menuCode": "category_config",
"resourceCode": "category_config",
@@ -4016,6 +4052,17 @@
}
]
},
{
"roleCode": "super_admin",
"menuCode": "online_monitor",
"menuBindType": 1,
"resourceTree": [
{
"code": "online_monitor",
"resourceBindType": 1
}
]
},
{
"roleCode": "super_admin",
"menuCode": "category_config",
@@ -4354,4 +4401,4 @@
"pageDailyPromptLimit": -1
}
]
}
}

View File

@@ -267,6 +267,11 @@ public enum ResourceEnum {
SANDBOX_CONFIG_DELETE(ResourceTypeEnum.OPERATION, "sandbox_config_delete", "删除", "sandbox_config"),
// SANDBOX_CONFIG_ENABLE(ResourceTypeEnum.OPERATION, "sandbox_config_enable", "启用/禁用", "sandbox_config"),
// ================== 在线监控模块 ==================
ONLINE_MONITOR(ResourceTypeEnum.MODULE, "online_monitor", "在线监控模块", "root"),
ONLINE_MONITOR_QUERY(ResourceTypeEnum.OPERATION, "online_monitor_query", "查询", "online_monitor"),
// ================== 分类管理模块 ==================
CATEGORY_CONFIG(ResourceTypeEnum.MODULE, "category_config", "分类管理模块", "root"),

View File

@@ -6,6 +6,7 @@ import com.xspaceagi.system.application.dto.UserDto;
import com.xspaceagi.system.application.dto.UserQueryDto;
import com.xspaceagi.system.application.service.NotifyMessageApplicationService;
import com.xspaceagi.system.application.service.SysGroupApplicationService;
import com.xspaceagi.system.application.service.SysRoleApplicationService;
import com.xspaceagi.system.application.service.UserApplicationService;
import com.xspaceagi.system.infra.dao.entity.SysGroup;
import com.xspaceagi.system.infra.dao.entity.NotifyMessage;
@@ -19,6 +20,7 @@ import com.xspaceagi.system.spec.dto.PageQueryVo;
import com.xspaceagi.system.spec.dto.ReqResult;
import com.xspaceagi.system.spec.enums.ErrorCodeEnum;
import com.xspaceagi.system.spec.enums.GroupEnum;
import com.xspaceagi.system.spec.enums.RoleEnum;
import com.xspaceagi.system.spec.exception.BizException;
import com.xspaceagi.system.spec.exception.BizExceptionCodeEnum;
import com.xspaceagi.system.web.dto.UserAddDto;
@@ -49,6 +51,9 @@ public class UserManageController {
@Resource
private SysGroupApplicationService sysGroupApplicationService;
@Resource
private SysRoleApplicationService sysRoleApplicationService;
@Resource
private NotifyMessageApplicationService notifyMessageApplicationService;
@@ -189,12 +194,16 @@ public class UserManageController {
}
private void checkCurrentTenantUser(Long userId) {
if (userId == null || userService.getById(userId) == null || !visibleUserIds().contains(userId)) {
User user = userId == null ? null : userService.getById(userId);
if (user == null || !isCurrentTenantUser(user) || (!isPlatformAdmin() && !visibleUserIds().contains(userId))) {
throw BizException.of(ErrorCodeEnum.PERMISSION_DENIED, BizExceptionCodeEnum.permissionDenied);
}
}
private void applyUserManageScope(PageQueryVo<UserQueryDto> pageQueryVo) {
if (isPlatformAdmin()) {
return;
}
if (pageQueryVo.getQueryFilter() == null) {
pageQueryVo.setQueryFilter(new UserQueryDto());
}
@@ -215,6 +224,18 @@ public class UserManageController {
.collect(Collectors.toSet());
}
private boolean isPlatformAdmin() {
UserDto currentUser = getCurrentUserDto();
return currentUser.getRole() == User.Role.Admin
&& (currentUserGroupIds().isEmpty()
|| sysRoleApplicationService.getRoleListByUserId(currentUser.getId()).stream()
.anyMatch(role -> RoleEnum.SUPER_ADMIN.getCode().equals(role.getCode())));
}
private boolean isCurrentTenantUser(User user) {
return user != null && (getCurrentUserDto().getTenantId() == null || getCurrentUserDto().getTenantId().equals(user.getTenantId()));
}
private UserDto getCurrentUserDto() {
UserDto userDto = (UserDto) RequestContext.get().getUser();
if (userDto == null) {

View File

@@ -8,6 +8,7 @@ import com.xspaceagi.system.application.converter.SysDataPermissionConverter;
import com.xspaceagi.system.application.dto.permission.*;
import com.xspaceagi.system.application.service.SysDataPermissionApplicationService;
import com.xspaceagi.system.application.service.SysGroupApplicationService;
import com.xspaceagi.system.application.service.SysRoleApplicationService;
import com.xspaceagi.system.application.service.SysSubjectPermissionApplicationService;
import com.xspaceagi.system.application.dto.UserDto;
import com.xspaceagi.system.domain.model.GroupBindMenuModel;
@@ -52,6 +53,8 @@ public class SysGroupController extends BaseController {
@Resource
private SysGroupApplicationService sysGroupApplicationService;
@Resource
private SysRoleApplicationService sysRoleApplicationService;
@Resource
private SysDataPermissionApplicationService sysDataPermissionApplicationService;
@Resource
private SysSubjectPermissionApplicationService sysSubjectPermissionApplicationService;
@@ -397,6 +400,9 @@ public class SysGroupController extends BaseController {
if (CollectionUtils.isEmpty(groupList)) {
return groupList;
}
if (isPlatformAdmin()) {
return groupList;
}
Set<Long> allowedGroupIds = currentUserGroupIds();
return groupList.stream()
.filter(group -> group.getId() != null && allowedGroupIds.contains(group.getId()))
@@ -404,6 +410,9 @@ public class SysGroupController extends BaseController {
}
private void checkGroupManageScope(Long groupId) {
if (isPlatformAdmin()) {
return;
}
if (groupId == null || !currentUserGroupIds().contains(groupId)) {
throw BizException.of(ErrorCodeEnum.PERMISSION_DENIED, BizExceptionCodeEnum.permissionDenied);
}
@@ -416,6 +425,14 @@ public class SysGroupController extends BaseController {
.collect(Collectors.toSet());
}
private boolean isPlatformAdmin() {
UserDto currentUser = getCurrentUserDto();
return currentUser.getRole() == User.Role.Admin
&& (currentUserGroupIds().isEmpty()
|| sysRoleApplicationService.getRoleListByUserId(currentUser.getId()).stream()
.anyMatch(role -> RoleEnum.SUPER_ADMIN.getCode().equals(role.getCode())));
}
private UserDto getCurrentUserDto() {
UserDto userDto = (UserDto) RequestContext.get().getUser();
if (userDto == null) {

View File

@@ -17,6 +17,7 @@ import com.xspaceagi.system.spec.common.RequestContext;
import com.xspaceagi.system.spec.dto.ReqResult;
import com.xspaceagi.system.spec.enums.ErrorCodeEnum;
import com.xspaceagi.system.spec.enums.GroupEnum;
import com.xspaceagi.system.spec.enums.RoleEnum;
import com.xspaceagi.system.spec.exception.BizException;
import com.xspaceagi.system.spec.exception.BizExceptionCodeEnum;
import com.xspaceagi.system.spec.utils.I18nUtil;
@@ -157,6 +158,9 @@ public class SysUserController extends BaseController {
if (targetUser == null || !Objects.equals(targetUser.getTenantId(), RequestContext.get().getTenantId())) {
throw BizException.of(ErrorCodeEnum.PERMISSION_DENIED, BizExceptionCodeEnum.permissionDenied);
}
if (isPlatformAdmin()) {
return;
}
Set<Long> allowedGroupIds = currentUserGroupIds();
boolean inScope = sysGroupApplicationService.getGroupListByUserId(userId).stream()
@@ -171,6 +175,9 @@ public class SysUserController extends BaseController {
if (CollectionUtils.isEmpty(groupIds)) {
return;
}
if (isPlatformAdmin()) {
return;
}
Set<Long> allowedGroupIds = currentUserGroupIds();
boolean allInScope = groupIds.stream().allMatch(allowedGroupIds::contains);
if (!allInScope) {
@@ -182,6 +189,9 @@ public class SysUserController extends BaseController {
if (CollectionUtils.isEmpty(roleIds)) {
return;
}
if (isPlatformAdmin()) {
return;
}
Set<Long> allowedRoleIds = sysRoleApplicationService.getRoleListByUserId(getCurrentUserDto().getId()).stream()
.map(SysRole::getId)
.collect(Collectors.toSet());
@@ -205,6 +215,14 @@ public class SysUserController extends BaseController {
.collect(Collectors.toSet());
}
private boolean isPlatformAdmin() {
UserDto currentUser = getCurrentUserDto();
return currentUser.getRole() == User.Role.Admin
&& (currentUserGroupIds().isEmpty()
|| sysRoleApplicationService.getRoleListByUserId(currentUser.getId()).stream()
.anyMatch(role -> RoleEnum.SUPER_ADMIN.getCode().equals(role.getCode())));
}
private UserDto getCurrentUserDto() {
UserDto userDto = (UserDto) RequestContext.get().getUser();
if (userDto == null) {

View File

@@ -0,0 +1,74 @@
-- Add system online monitor menu and resource.
-- Idempotent for existing tenants: creates menu/resource rows, binds menu to resource,
-- and grants super_admin default access. User-group admins can be granted this menu
-- from the database menu permission management UI.
INSERT INTO sys_resource (`parent_id`, `code`, `name`, `description`, `source`, `type`, `sort_index`, `status`, `_tenant_id`, `creator_id`, `creator`, `yn`)
SELECT 0, 'online_monitor', '在线监控模块', '在线监控模块', 1, 1, 17, 1, t._tenant_id, 0, 'system', 1
FROM (SELECT DISTINCT `_tenant_id` FROM sys_resource) t
WHERE NOT EXISTS (
SELECT 1 FROM sys_resource r
WHERE r.`_tenant_id` = t.`_tenant_id` AND r.`code` = 'online_monitor' AND r.`yn` = 1
);
INSERT INTO sys_resource (`parent_id`, `code`, `name`, `description`, `source`, `type`, `sort_index`, `status`, `_tenant_id`, `creator_id`, `creator`, `yn`)
SELECT parent.id, 'online_monitor_query', '查询', '查询在线监控', 1, 2, 1, 1, parent.`_tenant_id`, 0, 'system', 1
FROM sys_resource parent
WHERE parent.`code` = 'online_monitor' AND parent.`yn` = 1
AND NOT EXISTS (
SELECT 1 FROM sys_resource r
WHERE r.`_tenant_id` = parent.`_tenant_id` AND r.`code` = 'online_monitor_query' AND r.`yn` = 1
);
INSERT INTO sys_menu (`parent_id`, `code`, `name`, `description`, `source`, `path`, `open_type`, `icon`, `sort_index`, `status`, `_tenant_id`, `creator_id`, `creator`, `yn`)
SELECT parent.id, 'online_monitor', '在线监控', '在线监控', 1, '/system/online-monitor', 1, '', 11, 1, parent.`_tenant_id`, 0, 'system', 1
FROM sys_menu parent
WHERE parent.`code` = 'system_manage' AND parent.`yn` = 1
AND NOT EXISTS (
SELECT 1 FROM sys_menu m
WHERE m.`_tenant_id` = parent.`_tenant_id` AND m.`code` = 'online_monitor' AND m.`yn` = 1
);
INSERT INTO sys_menu_resource (`menu_id`, `resource_id`, `resource_bind_type`, `_tenant_id`, `creator_id`, `creator`, `yn`)
SELECT m.id, r.id, 1, m.`_tenant_id`, 0, 'system', 1
FROM sys_menu m
JOIN sys_resource r ON r.`_tenant_id` = m.`_tenant_id` AND r.`code` = 'online_monitor' AND r.`yn` = 1
WHERE m.`code` = 'online_monitor' AND m.`yn` = 1
AND NOT EXISTS (
SELECT 1 FROM sys_menu_resource mr
WHERE mr.`_tenant_id` = m.`_tenant_id` AND mr.`menu_id` = m.id AND mr.`resource_id` = r.id AND mr.`yn` = 1
);
UPDATE sys_menu_resource mr
JOIN sys_menu m ON m.id = mr.menu_id AND m.`_tenant_id` = mr.`_tenant_id` AND m.`code` = 'online_monitor' AND m.`yn` = 1
JOIN sys_resource r ON r.id = mr.resource_id AND r.`_tenant_id` = mr.`_tenant_id` AND r.`code` = 'online_monitor' AND r.`yn` = 1
SET mr.`resource_bind_type` = 1,
mr.`modified` = CURRENT_TIMESTAMP
WHERE mr.`yn` = 1;
INSERT INTO sys_role_menu (`role_id`, `menu_id`, `menu_bind_type`, `resource_tree_json`, `_tenant_id`, `creator_id`, `creator`, `yn`)
SELECT role.id,
menu.id,
1,
JSON_ARRAY(JSON_OBJECT('id', resource.id, 'code', resource.`code`, 'resourceBindType', 1)),
menu.`_tenant_id`,
0,
'system',
1
FROM sys_role role
JOIN sys_menu menu ON menu.`_tenant_id` = role.`_tenant_id` AND menu.`code` = 'online_monitor' AND menu.`yn` = 1
JOIN sys_resource resource ON resource.`_tenant_id` = role.`_tenant_id` AND resource.`code` = 'online_monitor' AND resource.`yn` = 1
WHERE role.`code` = 'super_admin' AND role.`yn` = 1
AND NOT EXISTS (
SELECT 1 FROM sys_role_menu rm
WHERE rm.`_tenant_id` = role.`_tenant_id` AND rm.`role_id` = role.id AND rm.`menu_id` = menu.id AND rm.`yn` = 1
);
UPDATE sys_role_menu rm
JOIN sys_role role ON role.id = rm.role_id AND role.`_tenant_id` = rm.`_tenant_id` AND role.`code` = 'super_admin' AND role.`yn` = 1
JOIN sys_menu menu ON menu.id = rm.menu_id AND menu.`_tenant_id` = rm.`_tenant_id` AND menu.`code` = 'online_monitor' AND menu.`yn` = 1
JOIN sys_resource resource ON resource.`_tenant_id` = rm.`_tenant_id` AND resource.`code` = 'online_monitor' AND resource.`yn` = 1
SET rm.`menu_bind_type` = 1,
rm.`resource_tree_json` = JSON_ARRAY(JSON_OBJECT('id', resource.id, 'code', resource.`code`, 'resourceBindType', 1)),
rm.`modified` = CURRENT_TIMESTAMP
WHERE rm.`yn` = 1;

View File

@@ -4815,6 +4815,7 @@ export const EN_US: SystemLangMap = {
"PC.Routes.permissionResources": "Permission Resources",
"PC.Routes.publicModelManagement": "Public Model Mgmt",
"PC.Routes.modelMonitor": "Model Monitor",
"PC.Routes.onlineMonitor": "Online Monitor",
"PC.Routes.publishAudit": "Publish Audit",
"PC.Routes.publishedManagement": "Published Mgmt",
"PC.Routes.roleManage": "Role Mgmt",
@@ -4828,6 +4829,39 @@ export const EN_US: SystemLangMap = {
"PC.Routes.themeConfig": "Theme Config",
"PC.Routes.userGroupManage": "User Group Mgmt",
"PC.Routes.userManagement": "User Mgmt",
"PC.Pages.OnlineMonitor.authorization": "Authorization",
"PC.Pages.OnlineMonitor.clientCapacity": "Client Capacity",
"PC.Pages.OnlineMonitor.clientCount": "Clients",
"PC.Pages.OnlineMonitor.clientName": "Client Name",
"PC.Pages.OnlineMonitor.clientTab": "Client Status",
"PC.Pages.OnlineMonitor.created": "Created",
"PC.Pages.OnlineMonitor.disabled": "Disabled",
"PC.Pages.OnlineMonitor.disabledUsers": "Disabled Users",
"PC.Pages.OnlineMonitor.enabled": "Enabled",
"PC.Pages.OnlineMonitor.enabledUsers": "Enabled Users",
"PC.Pages.OnlineMonitor.expireTime": "Expires At",
"PC.Pages.OnlineMonitor.fetchFailed": "Failed to load online monitor data",
"PC.Pages.OnlineMonitor.groupName": "User Group",
"PC.Pages.OnlineMonitor.groups": "Groups",
"PC.Pages.OnlineMonitor.groupTab": "Group Capacity",
"PC.Pages.OnlineMonitor.inactive": "Inactive",
"PC.Pages.OnlineMonitor.inactiveClients": "Inactive Clients",
"PC.Pages.OnlineMonitor.lastLoginTime": "Last Login",
"PC.Pages.OnlineMonitor.maxAgentCount": "Max Sessions",
"PC.Pages.OnlineMonitor.modified": "Updated",
"PC.Pages.OnlineMonitor.nickName": "Nickname",
"PC.Pages.OnlineMonitor.offline": "Offline",
"PC.Pages.OnlineMonitor.offlineClients": "Offline Clients",
"PC.Pages.OnlineMonitor.online": "Online",
"PC.Pages.OnlineMonitor.onlineClients": "Online Clients",
"PC.Pages.OnlineMonitor.pageTitle": "Online Monitor",
"PC.Pages.OnlineMonitor.status": "Status",
"PC.Pages.OnlineMonitor.totalClients": "Total Clients",
"PC.Pages.OnlineMonitor.totalUsers": "Total Users",
"PC.Pages.OnlineMonitor.unlimited": "Unlimited",
"PC.Pages.OnlineMonitor.userCapacity": "User Capacity",
"PC.Pages.OnlineMonitor.userName": "Username",
"PC.Pages.OnlineMonitor.userTab": "User Status",
"PC.Routes.devPaymentEarnings": "Payment & Earnings (Dev)",
"PC.Routes.adminSubscriptionCredits": "Subscriptions & Credits (Admin)",
"PC.Routes.paymentConfig": "Payment Config",

View File

@@ -4834,6 +4834,7 @@ export const ZH_CN: SystemLangMap = {
"PC.Routes.permissionResources": "权限资源",
"PC.Routes.publicModelManagement": "公共模型管理",
"PC.Routes.modelMonitor": "模型监控",
"PC.Routes.onlineMonitor": "在线监控",
"PC.Routes.publishAudit": "发布审核",
"PC.Routes.publishedManagement": "已发布管理",
"PC.Routes.roleManage": "角色管理",
@@ -4847,6 +4848,39 @@ export const ZH_CN: SystemLangMap = {
"PC.Routes.themeConfig": "主题配置",
"PC.Routes.userGroupManage": "单位(部门)管理",
"PC.Routes.userManagement": "用户管理",
"PC.Pages.OnlineMonitor.authorization": "授权状态",
"PC.Pages.OnlineMonitor.clientCapacity": "客户端容量",
"PC.Pages.OnlineMonitor.clientCount": "客户端数",
"PC.Pages.OnlineMonitor.clientName": "客户端名称",
"PC.Pages.OnlineMonitor.clientTab": "客户端状态",
"PC.Pages.OnlineMonitor.created": "创建时间",
"PC.Pages.OnlineMonitor.disabled": "已禁用",
"PC.Pages.OnlineMonitor.disabledUsers": "禁用用户",
"PC.Pages.OnlineMonitor.enabled": "已启用",
"PC.Pages.OnlineMonitor.enabledUsers": "启用用户",
"PC.Pages.OnlineMonitor.expireTime": "到期时间",
"PC.Pages.OnlineMonitor.fetchFailed": "获取在线监控信息失败",
"PC.Pages.OnlineMonitor.groupName": "用户组",
"PC.Pages.OnlineMonitor.groups": "所属用户组",
"PC.Pages.OnlineMonitor.groupTab": "用户组容量",
"PC.Pages.OnlineMonitor.inactive": "已停用",
"PC.Pages.OnlineMonitor.inactiveClients": "停用客户端",
"PC.Pages.OnlineMonitor.lastLoginTime": "最后登录时间",
"PC.Pages.OnlineMonitor.maxAgentCount": "最大会话数",
"PC.Pages.OnlineMonitor.modified": "更新时间",
"PC.Pages.OnlineMonitor.nickName": "昵称",
"PC.Pages.OnlineMonitor.offline": "离线",
"PC.Pages.OnlineMonitor.offlineClients": "离线客户端",
"PC.Pages.OnlineMonitor.online": "在线",
"PC.Pages.OnlineMonitor.onlineClients": "在线客户端",
"PC.Pages.OnlineMonitor.pageTitle": "在线监控",
"PC.Pages.OnlineMonitor.status": "状态",
"PC.Pages.OnlineMonitor.totalClients": "客户端总数",
"PC.Pages.OnlineMonitor.totalUsers": "用户总数",
"PC.Pages.OnlineMonitor.unlimited": "不限",
"PC.Pages.OnlineMonitor.userCapacity": "用户容量",
"PC.Pages.OnlineMonitor.userName": "用户名",
"PC.Pages.OnlineMonitor.userTab": "用户状态",
"PC.Routes.devPaymentEarnings": "支付与收益(开发者)",
"PC.Routes.adminSubscriptionCredits": "订阅与积分(管理员)",
"PC.Routes.paymentConfig": "支付配置",

View File

@@ -4628,6 +4628,7 @@ export const ZH_TW: SystemLangMap = {
"PC.Routes.permissionResources": "許可權資源",
"PC.Routes.publicModelManagement": "公共模型管理",
"PC.Routes.modelMonitor": "模型監控",
"PC.Routes.onlineMonitor": "線上監控",
"PC.Routes.publishAudit": "釋出稽核",
"PC.Routes.publishedManagement": "已釋出管理",
"PC.Routes.roleManage": "角色管理",
@@ -4641,6 +4642,39 @@ export const ZH_TW: SystemLangMap = {
"PC.Routes.themeConfig": "主題設定",
"PC.Routes.userGroupManage": "使用者組管理",
"PC.Routes.userManagement": "使用者管理",
"PC.Pages.OnlineMonitor.authorization": "授權狀態",
"PC.Pages.OnlineMonitor.clientCapacity": "客戶端容量",
"PC.Pages.OnlineMonitor.clientCount": "客戶端數",
"PC.Pages.OnlineMonitor.clientName": "客戶端名稱",
"PC.Pages.OnlineMonitor.clientTab": "客戶端狀態",
"PC.Pages.OnlineMonitor.created": "建立時間",
"PC.Pages.OnlineMonitor.disabled": "已停用",
"PC.Pages.OnlineMonitor.disabledUsers": "停用使用者",
"PC.Pages.OnlineMonitor.enabled": "已啟用",
"PC.Pages.OnlineMonitor.enabledUsers": "啟用使用者",
"PC.Pages.OnlineMonitor.expireTime": "到期時間",
"PC.Pages.OnlineMonitor.fetchFailed": "獲取線上監控資訊失敗",
"PC.Pages.OnlineMonitor.groupName": "使用者組",
"PC.Pages.OnlineMonitor.groups": "所屬使用者組",
"PC.Pages.OnlineMonitor.groupTab": "使用者組容量",
"PC.Pages.OnlineMonitor.inactive": "已停用",
"PC.Pages.OnlineMonitor.inactiveClients": "停用客戶端",
"PC.Pages.OnlineMonitor.lastLoginTime": "最後登入時間",
"PC.Pages.OnlineMonitor.maxAgentCount": "最大會話數",
"PC.Pages.OnlineMonitor.modified": "更新時間",
"PC.Pages.OnlineMonitor.nickName": "暱稱",
"PC.Pages.OnlineMonitor.offline": "離線",
"PC.Pages.OnlineMonitor.offlineClients": "離線客戶端",
"PC.Pages.OnlineMonitor.online": "線上",
"PC.Pages.OnlineMonitor.onlineClients": "線上客戶端",
"PC.Pages.OnlineMonitor.pageTitle": "線上監控",
"PC.Pages.OnlineMonitor.status": "狀態",
"PC.Pages.OnlineMonitor.totalClients": "客戶端總數",
"PC.Pages.OnlineMonitor.totalUsers": "使用者總數",
"PC.Pages.OnlineMonitor.unlimited": "不限",
"PC.Pages.OnlineMonitor.userCapacity": "使用者容量",
"PC.Pages.OnlineMonitor.userName": "使用者名稱",
"PC.Pages.OnlineMonitor.userTab": "使用者狀態",
"PC.Routes.devPaymentEarnings": "支付與收益(開發者)",
"PC.Routes.adminSubscriptionCredits": "訂閱與積分(管理員)",
"PC.Routes.paymentConfig": "支付設定",

View File

@@ -0,0 +1,16 @@
.summaryRow {
margin-bottom: 16px;
}
.summaryCard {
height: 100%;
border-radius: 8px;
}
.tabs {
:global {
.ant-tabs-nav {
margin-bottom: 12px;
}
}
}

View File

@@ -0,0 +1,351 @@
import WorkspaceLayout from '@/components/WorkspaceLayout';
import { SUCCESS_CODE } from '@/constants/codes.constants';
import { dict } from '@/services/i18nRuntime';
import { apiGetOnlineMonitor } from '@/services/systemManage';
import type {
OnlineMonitorClient,
OnlineMonitorGroup,
OnlineMonitorInfo,
OnlineMonitorUser,
} from '@/types/interfaces/systemManage';
import { ReloadOutlined } from '@ant-design/icons';
import type { ProColumns } from '@ant-design/pro-components';
import { ProTable } from '@ant-design/pro-components';
import {
Button,
Card,
Col,
Row,
Space,
Statistic,
Tabs,
Tag,
message,
} from 'antd';
import dayjs from 'dayjs';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import styles from './index.less';
const unlimitedText = (value?: number) =>
value === undefined || value === null || value < 0
? dict('PC.Pages.OnlineMonitor.unlimited')
: value;
const formatTime = (value?: string) =>
value ? dayjs(value).format('YYYY-MM-DD HH:mm') : '-';
const OnlineMonitor: React.FC = () => {
const [loading, setLoading] = useState(false);
const [data, setData] = useState<OnlineMonitorInfo>();
const fetchData = useCallback(async () => {
setLoading(true);
try {
const res = await apiGetOnlineMonitor();
if (res.code === SUCCESS_CODE) {
setData(res.data);
} else {
message.error(
res.message || dict('PC.Pages.OnlineMonitor.fetchFailed'),
);
}
} finally {
setLoading(false);
}
}, []);
useEffect(() => {
fetchData();
}, [fetchData]);
const summaryItems = useMemo(
() => [
{
title: dict('PC.Pages.OnlineMonitor.totalClients'),
value: data?.totalClientCount || 0,
},
{
title: dict('PC.Pages.OnlineMonitor.onlineClients'),
value: data?.onlineClientCount || 0,
valueStyle: { color: '#16a34a' },
},
{
title: dict('PC.Pages.OnlineMonitor.offlineClients'),
value: data?.offlineClientCount || 0,
valueStyle: { color: '#ea580c' },
},
{
title: dict('PC.Pages.OnlineMonitor.inactiveClients'),
value: data?.inactiveClientCount || 0,
valueStyle: { color: '#64748b' },
},
{
title: dict('PC.Pages.OnlineMonitor.totalUsers'),
value: data?.totalUserCount || 0,
},
{
title: dict('PC.Pages.OnlineMonitor.enabledUsers'),
value: data?.enabledUserCount || 0,
valueStyle: { color: '#2563eb' },
},
{
title: dict('PC.Pages.OnlineMonitor.disabledUsers'),
value: data?.disabledUserCount || 0,
valueStyle: { color: '#dc2626' },
},
],
[data],
);
const groupColumns: ProColumns<OnlineMonitorGroup>[] = [
{
title: dict('PC.Pages.OnlineMonitor.groupName'),
dataIndex: 'groupName',
width: 160,
fixed: 'left',
},
{
title: dict('PC.Pages.OnlineMonitor.authorization'),
dataIndex: 'authEnabled',
width: 100,
render: (_, record) => (
<Tag color={record.authEnabled === 1 ? 'green' : 'default'}>
{record.authEnabled === 1
? dict('PC.Pages.OnlineMonitor.enabled')
: dict('PC.Pages.OnlineMonitor.disabled')}
</Tag>
),
},
{
title: dict('PC.Pages.OnlineMonitor.expireTime'),
dataIndex: 'expireTime',
width: 160,
render: (_, record) => formatTime(record.expireTime),
},
{
title: dict('PC.Pages.OnlineMonitor.userCapacity'),
width: 120,
render: (_, record) =>
`${record.currentUserCount}/${unlimitedText(record.maxUserCount)}`,
},
{
title: dict('PC.Pages.OnlineMonitor.clientCapacity'),
width: 120,
render: (_, record) =>
`${record.currentClientCount}/${unlimitedText(record.maxClientCount)}`,
},
{
title: dict('PC.Pages.OnlineMonitor.onlineClients'),
dataIndex: 'onlineClientCount',
width: 110,
},
{
title: dict('PC.Pages.OnlineMonitor.offlineClients'),
dataIndex: 'offlineClientCount',
width: 110,
},
{
title: dict('PC.Pages.OnlineMonitor.inactiveClients'),
dataIndex: 'inactiveClientCount',
width: 110,
},
];
const clientColumns: ProColumns<OnlineMonitorClient>[] = [
{
title: dict('PC.Pages.OnlineMonitor.clientName'),
dataIndex: 'name',
width: 180,
fixed: 'left',
},
{
title: dict('PC.Pages.OnlineMonitor.status'),
width: 100,
render: (_, record) => (
<Tag
color={
!record.isActive ? 'default' : record.online ? 'green' : 'orange'
}
>
{!record.isActive
? dict('PC.Pages.OnlineMonitor.inactive')
: record.online
? dict('PC.Pages.OnlineMonitor.online')
: dict('PC.Pages.OnlineMonitor.offline')}
</Tag>
),
},
{
title: dict('PC.Pages.OnlineMonitor.userName'),
dataIndex: 'userName',
width: 140,
},
{
title: dict('PC.Pages.OnlineMonitor.nickName'),
dataIndex: 'nickName',
width: 140,
},
{
title: dict('PC.Pages.OnlineMonitor.groupName'),
dataIndex: 'groupName',
width: 160,
},
{
title: dict('PC.Pages.OnlineMonitor.maxAgentCount'),
dataIndex: 'maxAgentCount',
width: 140,
render: (_, record) => unlimitedText(record.maxAgentCount),
},
{
title: dict('PC.Pages.OnlineMonitor.created'),
dataIndex: 'created',
width: 160,
render: (_, record) => formatTime(record.created),
},
{
title: dict('PC.Pages.OnlineMonitor.modified'),
dataIndex: 'modified',
width: 160,
render: (_, record) => formatTime(record.modified),
},
];
const userColumns: ProColumns<OnlineMonitorUser>[] = [
{
title: dict('PC.Pages.OnlineMonitor.userName'),
dataIndex: 'userName',
width: 150,
fixed: 'left',
},
{
title: dict('PC.Pages.OnlineMonitor.nickName'),
dataIndex: 'nickName',
width: 140,
},
{
title: dict('PC.Pages.OnlineMonitor.status'),
width: 100,
render: (_, record) => (
<Tag color={record.status === 'Enabled' ? 'green' : 'red'}>
{record.status === 'Enabled'
? dict('PC.Pages.OnlineMonitor.enabled')
: dict('PC.Pages.OnlineMonitor.disabled')}
</Tag>
),
},
{
title: dict('PC.Pages.OnlineMonitor.groups'),
width: 220,
render: (_, record) => (
<Space wrap>
{(record.groupNames || []).map((name) => (
<Tag key={name}>{name}</Tag>
))}
</Space>
),
},
{
title: dict('PC.Pages.OnlineMonitor.clientCount'),
dataIndex: 'clientCount',
width: 110,
},
{
title: dict('PC.Pages.OnlineMonitor.onlineClients'),
dataIndex: 'onlineClientCount',
width: 110,
},
{
title: dict('PC.Pages.OnlineMonitor.inactiveClients'),
dataIndex: 'inactiveClientCount',
width: 110,
},
{
title: dict('PC.Pages.OnlineMonitor.lastLoginTime'),
dataIndex: 'lastLoginTime',
width: 160,
render: (_, record) => formatTime(record.lastLoginTime),
},
];
return (
<WorkspaceLayout
title={dict('PC.Pages.OnlineMonitor.pageTitle')}
rightSlot={
<Button icon={<ReloadOutlined />} loading={loading} onClick={fetchData}>
{dict('PC.Common.Global.refresh')}
</Button>
}
contentPadding="16px 24px"
>
<Row gutter={[16, 16]} className={styles.summaryRow}>
{summaryItems.map((item) => (
<Col xs={12} sm={8} md={6} xl={3} key={item.title}>
<Card size="small" className={styles.summaryCard}>
<Statistic
title={item.title}
value={item.value}
valueStyle={item.valueStyle}
/>
</Card>
</Col>
))}
</Row>
<Tabs
className={styles.tabs}
items={[
{
key: 'groups',
label: dict('PC.Pages.OnlineMonitor.groupTab'),
children: (
<ProTable
rowKey="groupId"
search={false}
options={false}
loading={loading}
columns={groupColumns}
dataSource={data?.groups || []}
pagination={{ pageSize: 10 }}
scroll={{ x: 980 }}
/>
),
},
{
key: 'clients',
label: dict('PC.Pages.OnlineMonitor.clientTab'),
children: (
<ProTable
rowKey="id"
search={false}
options={false}
loading={loading}
columns={clientColumns}
dataSource={data?.clients || []}
pagination={{ pageSize: 10 }}
scroll={{ x: 1180 }}
/>
),
},
{
key: 'users',
label: dict('PC.Pages.OnlineMonitor.userTab'),
children: (
<ProTable
rowKey="userId"
search={false}
options={false}
loading={loading}
columns={userColumns}
dataSource={data?.users || []}
pagination={{ pageSize: 10 }}
scroll={{ x: 1100 }}
/>
),
},
]}
/>
</WorkspaceLayout>
);
};
export default OnlineMonitor;

View File

@@ -259,6 +259,11 @@ const routes = [
name: getRouteLabel('PC.Routes.modelMonitor'),
component: '@/pages/SystemManagement/ModelMonitor',
},
{
path: 'online-monitor',
name: getRouteLabel('PC.Routes.onlineMonitor'),
component: '@/pages/SystemManagement/OnlineMonitor',
},
{
path: 'config',
name: getRouteLabel('PC.Routes.systemConfig'),

View File

@@ -59,6 +59,8 @@ const MENU_ICON_MAP: Record<string, string> = {
admin_subscription_credits: 'icons-nav-subscription',
// 模型监控
model_monitor: 'icons-common-console',
// 在线监控
online_monitor: 'icons-nav-computer',
// 二级菜单 - 工作空间
// 智能体开发

View File

@@ -10,6 +10,7 @@ import type {
ConversationStatsResult,
ModelConfigDto,
NotifyMessageSendParams,
OnlineMonitorInfo,
PayConfigResult,
PayConnectivityResult,
PublishedDto,
@@ -567,6 +568,17 @@ export async function apiGetSandboxConfigList(): Promise<
});
}
/**
* 查询在线监控信息
*/
export async function apiGetOnlineMonitor(): Promise<
RequestResponse<OnlineMonitorInfo>
> {
return request('/api/system/sandbox/config/online-monitor', {
method: 'GET',
});
}
/**
* 创建沙盒配置
*/

View File

@@ -837,6 +837,66 @@ export interface SandboxGlobalConfig {
perUserCpuCores: number | string;
}
/** 在线监控聚合信息 */
export interface OnlineMonitorInfo {
totalClientCount: number;
onlineClientCount: number;
offlineClientCount: number;
inactiveClientCount: number;
totalUserCount: number;
enabledUserCount: number;
disabledUserCount: number;
groups: OnlineMonitorGroup[];
users: OnlineMonitorUser[];
clients: OnlineMonitorClient[];
}
/** 在线监控-用户组 */
export interface OnlineMonitorGroup {
groupId: number;
groupName: string;
authEnabled?: number;
status?: number;
expireTime?: string;
maxUserCount?: number;
currentUserCount: number;
maxClientCount?: number;
currentClientCount: number;
onlineClientCount: number;
offlineClientCount: number;
inactiveClientCount: number;
}
/** 在线监控-用户 */
export interface OnlineMonitorUser {
userId: number;
userName: string;
nickName?: string;
role?: string;
status?: string;
groupNames?: string[];
clientCount: number;
onlineClientCount: number;
inactiveClientCount: number;
lastLoginTime?: string;
}
/** 在线监控-客户端 */
export interface OnlineMonitorClient {
id: number;
name: string;
userId?: number;
userName?: string;
nickName?: string;
groupId?: number;
groupName?: string;
isActive?: boolean;
online: boolean;
maxAgentCount?: number;
created?: string;
modified?: string;
}
/**
* 沙盒选择项(用于电脑选择器下拉)
*/