diff --git a/qiming-backend/app-platform-modules/app-platform-sandbox/app-platform-sandbox-application/src/main/java/com/xspaceagi/sandbox/application/dto/OnlineMonitorDto.java b/qiming-backend/app-platform-modules/app-platform-sandbox/app-platform-sandbox-application/src/main/java/com/xspaceagi/sandbox/application/dto/OnlineMonitorDto.java new file mode 100644 index 00000000..004860dc --- /dev/null +++ b/qiming-backend/app-platform-modules/app-platform-sandbox/app-platform-sandbox-application/src/main/java/com/xspaceagi/sandbox/application/dto/OnlineMonitorDto.java @@ -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 groups; + + @Schema(description = "用户监控信息") + private List users; + + @Schema(description = "客户端监控信息") + private List 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 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; + } +} diff --git a/qiming-backend/app-platform-modules/app-platform-sandbox/app-platform-sandbox-ui/src/main/java/com/xspaceagi/sandbox/ui/web/controller/SandboxConfigManageController.java b/qiming-backend/app-platform-modules/app-platform-sandbox/app-platform-sandbox-ui/src/main/java/com/xspaceagi/sandbox/ui/web/controller/SandboxConfigManageController.java index ab4d4a90..023c0e5e 100644 --- a/qiming-backend/app-platform-modules/app-platform-sandbox/app-platform-sandbox-ui/src/main/java/com/xspaceagi/sandbox/ui/web/controller/SandboxConfigManageController.java +++ b/qiming-backend/app-platform-modules/app-platform-sandbox/app-platform-sandbox-ui/src/main/java/com/xspaceagi/sandbox/ui/web/controller/SandboxConfigManageController.java @@ -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 onlineMonitor() { + boolean platformAdmin = isPlatformAdmin(); + List groups = visibleGroups(); + Set groupIds = groups.stream().map(SysGroup::getId).collect(Collectors.toSet()); + + List users = visibleUsers(platformAdmin, groupIds); + Set 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 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 groups, List users, List configs) { + Map groupMap = groups.stream() + .filter(group -> group.getId() != null) + .collect(Collectors.toMap(SysGroup::getId, group -> group, (a, b) -> a)); + Map userMap = users.stream() + .filter(user -> user.getId() != null) + .collect(Collectors.toMap(User::getId, user -> user, (a, b) -> a)); + Map> 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 users, List configs) { + Set groupUserIds = sysGroupApplicationService.getUserListByGroupId(group.getId()).stream() + .map(User::getId) + .collect(Collectors.toSet()); + List 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> userGroupMap, List configs) { + List 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 userMap, Map 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 visibleGroups() { + if (isPlatformAdmin()) { + return sysGroupApplicationService.getGroupList(new SysGroup()).stream() + .filter(group -> !GroupEnum.DEFAULT_GROUP.getCode().equals(group.getCode())) + .toList(); + } + Set groupIds = currentUserGroupIds(); + if (groupIds.isEmpty()) { + return List.of(); + } + return sysGroupApplicationService.listGroupsByIds(new ArrayList<>(groupIds)); + } + + private List visibleUsers(boolean platformAdmin, Set 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 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() { diff --git a/qiming-backend/app-platform-modules/platform-system/system-application/src/main/resources/permission/permission-1.4.json b/qiming-backend/app-platform-modules/platform-system/system-application/src/main/resources/permission/permission-1.4.json index 119c51e7..9e38ff9f 100644 --- a/qiming-backend/app-platform-modules/platform-system/system-application/src/main/resources/permission/permission-1.4.json +++ b/qiming-backend/app-platform-modules/platform-system/system-application/src/main/resources/permission/permission-1.4.json @@ -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 } ] -} \ No newline at end of file +} diff --git a/qiming-backend/app-platform-modules/platform-system/system-spec/src/main/java/com/xspaceagi/system/spec/enums/ResourceEnum.java b/qiming-backend/app-platform-modules/platform-system/system-spec/src/main/java/com/xspaceagi/system/spec/enums/ResourceEnum.java index a70fc37a..d6269ed0 100644 --- a/qiming-backend/app-platform-modules/platform-system/system-spec/src/main/java/com/xspaceagi/system/spec/enums/ResourceEnum.java +++ b/qiming-backend/app-platform-modules/platform-system/system-spec/src/main/java/com/xspaceagi/system/spec/enums/ResourceEnum.java @@ -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"), diff --git a/qiming-backend/app-platform-modules/platform-system/system-web/src/main/java/com/xspaceagi/system/web/controller/UserManageController.java b/qiming-backend/app-platform-modules/platform-system/system-web/src/main/java/com/xspaceagi/system/web/controller/UserManageController.java index 0b0903eb..fa319323 100644 --- a/qiming-backend/app-platform-modules/platform-system/system-web/src/main/java/com/xspaceagi/system/web/controller/UserManageController.java +++ b/qiming-backend/app-platform-modules/platform-system/system-web/src/main/java/com/xspaceagi/system/web/controller/UserManageController.java @@ -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 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) { diff --git a/qiming-backend/app-platform-modules/platform-system/system-web/src/main/java/com/xspaceagi/system/web/controller/permission/SysGroupController.java b/qiming-backend/app-platform-modules/platform-system/system-web/src/main/java/com/xspaceagi/system/web/controller/permission/SysGroupController.java index 76689432..a5d2a29a 100644 --- a/qiming-backend/app-platform-modules/platform-system/system-web/src/main/java/com/xspaceagi/system/web/controller/permission/SysGroupController.java +++ b/qiming-backend/app-platform-modules/platform-system/system-web/src/main/java/com/xspaceagi/system/web/controller/permission/SysGroupController.java @@ -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 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) { diff --git a/qiming-backend/app-platform-modules/platform-system/system-web/src/main/java/com/xspaceagi/system/web/controller/permission/SysUserController.java b/qiming-backend/app-platform-modules/platform-system/system-web/src/main/java/com/xspaceagi/system/web/controller/permission/SysUserController.java index b8de0956..81fb33ca 100644 --- a/qiming-backend/app-platform-modules/platform-system/system-web/src/main/java/com/xspaceagi/system/web/controller/permission/SysUserController.java +++ b/qiming-backend/app-platform-modules/platform-system/system-web/src/main/java/com/xspaceagi/system/web/controller/permission/SysUserController.java @@ -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 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 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 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) { diff --git a/qiming-backend/sql/update-20260602.sql b/qiming-backend/sql/update-20260602.sql new file mode 100644 index 00000000..f7964a6e --- /dev/null +++ b/qiming-backend/sql/update-20260602.sql @@ -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; diff --git a/qiming/src/locales/i18n/en-US.ts b/qiming/src/locales/i18n/en-US.ts index 87a3895e..f9f9e5bc 100644 --- a/qiming/src/locales/i18n/en-US.ts +++ b/qiming/src/locales/i18n/en-US.ts @@ -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", diff --git a/qiming/src/locales/i18n/zh-CN.ts b/qiming/src/locales/i18n/zh-CN.ts index 0416cb09..2cae6eb3 100644 --- a/qiming/src/locales/i18n/zh-CN.ts +++ b/qiming/src/locales/i18n/zh-CN.ts @@ -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": "支付配置", diff --git a/qiming/src/locales/i18n/zh-TW.ts b/qiming/src/locales/i18n/zh-TW.ts index c61c2508..ba8f1c95 100644 --- a/qiming/src/locales/i18n/zh-TW.ts +++ b/qiming/src/locales/i18n/zh-TW.ts @@ -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": "支付設定", diff --git a/qiming/src/pages/SystemManagement/OnlineMonitor/index.less b/qiming/src/pages/SystemManagement/OnlineMonitor/index.less new file mode 100644 index 00000000..6680257f --- /dev/null +++ b/qiming/src/pages/SystemManagement/OnlineMonitor/index.less @@ -0,0 +1,16 @@ +.summaryRow { + margin-bottom: 16px; +} + +.summaryCard { + height: 100%; + border-radius: 8px; +} + +.tabs { + :global { + .ant-tabs-nav { + margin-bottom: 12px; + } + } +} diff --git a/qiming/src/pages/SystemManagement/OnlineMonitor/index.tsx b/qiming/src/pages/SystemManagement/OnlineMonitor/index.tsx new file mode 100644 index 00000000..0477882e --- /dev/null +++ b/qiming/src/pages/SystemManagement/OnlineMonitor/index.tsx @@ -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(); + + 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[] = [ + { + title: dict('PC.Pages.OnlineMonitor.groupName'), + dataIndex: 'groupName', + width: 160, + fixed: 'left', + }, + { + title: dict('PC.Pages.OnlineMonitor.authorization'), + dataIndex: 'authEnabled', + width: 100, + render: (_, record) => ( + + {record.authEnabled === 1 + ? dict('PC.Pages.OnlineMonitor.enabled') + : dict('PC.Pages.OnlineMonitor.disabled')} + + ), + }, + { + 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[] = [ + { + title: dict('PC.Pages.OnlineMonitor.clientName'), + dataIndex: 'name', + width: 180, + fixed: 'left', + }, + { + title: dict('PC.Pages.OnlineMonitor.status'), + width: 100, + render: (_, record) => ( + + {!record.isActive + ? dict('PC.Pages.OnlineMonitor.inactive') + : record.online + ? dict('PC.Pages.OnlineMonitor.online') + : dict('PC.Pages.OnlineMonitor.offline')} + + ), + }, + { + 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[] = [ + { + 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) => ( + + {record.status === 'Enabled' + ? dict('PC.Pages.OnlineMonitor.enabled') + : dict('PC.Pages.OnlineMonitor.disabled')} + + ), + }, + { + title: dict('PC.Pages.OnlineMonitor.groups'), + width: 220, + render: (_, record) => ( + + {(record.groupNames || []).map((name) => ( + {name} + ))} + + ), + }, + { + 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 ( + } loading={loading} onClick={fetchData}> + {dict('PC.Common.Global.refresh')} + + } + contentPadding="16px 24px" + > + + {summaryItems.map((item) => ( + + + + + + ))} + + + + ), + }, + { + key: 'clients', + label: dict('PC.Pages.OnlineMonitor.clientTab'), + children: ( + + ), + }, + { + key: 'users', + label: dict('PC.Pages.OnlineMonitor.userTab'), + children: ( + + ), + }, + ]} + /> + + ); +}; + +export default OnlineMonitor; diff --git a/qiming/src/routes/index.ts b/qiming/src/routes/index.ts index e8b2548d..3cd8560f 100644 --- a/qiming/src/routes/index.ts +++ b/qiming/src/routes/index.ts @@ -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'), diff --git a/qiming/src/services/menuService.ts b/qiming/src/services/menuService.ts index 62cf101d..6c14e033 100644 --- a/qiming/src/services/menuService.ts +++ b/qiming/src/services/menuService.ts @@ -59,6 +59,8 @@ const MENU_ICON_MAP: Record = { admin_subscription_credits: 'icons-nav-subscription', // 模型监控 model_monitor: 'icons-common-console', + // 在线监控 + online_monitor: 'icons-nav-computer', // 二级菜单 - 工作空间 // 智能体开发 diff --git a/qiming/src/services/systemManage.ts b/qiming/src/services/systemManage.ts index a42d77a9..6b8bdaff 100644 --- a/qiming/src/services/systemManage.ts +++ b/qiming/src/services/systemManage.ts @@ -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 +> { + return request('/api/system/sandbox/config/online-monitor', { + method: 'GET', + }); +} + /** * 创建沙盒配置 */ diff --git a/qiming/src/types/interfaces/systemManage.ts b/qiming/src/types/interfaces/systemManage.ts index c7d67bdc..6cf52cd5 100644 --- a/qiming/src/types/interfaces/systemManage.ts +++ b/qiming/src/types/interfaces/systemManage.ts @@ -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; +} + /** * 沙盒选择项(用于电脑选择器下拉) */