content: package org.ruoyi.system.controller.monitor;
  
  import cn.dev33.satoken.annotation.SaCheckPermission;
  import cn.dev33.satoken.exception.NotLoginException;
  import cn.dev33.satoken.stp.StpUtil;
  import cn.hutool.core.bean.BeanUtil;
  import lombok.RequiredArgsConstructor;
  import org.ruoyi.common.core.constant.CacheConstants;
  import org.ruoyi.common.core.domain.R;
  import org.ruoyi.common.core.domain.dto.UserOnlineDTO;
  import org.ruoyi.common.core.utils.StreamUtils;
  import org.ruoyi.common.core.utils.StringUtils;
  import org.ruoyi.common.log.annotation.Log;
  import org.ruoyi.common.log.enums.BusinessType;
  import org.ruoyi.common.redis.utils.RedisUtils;
  import org.ruoyi.common.web.core.BaseController;
  import org.ruoyi.core.page.TableDataInfo;
  import org.ruoyi.system.domain.SysUserOnline;
  import org.springframework.web.bind.annotation.*;
  
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.List;
  
  /**
   * 在线用户监控
   *
   * @author Lion Li
   */
  @RequiredArgsConstructor
  @RestController
  @RequestMapping("/monitor/online")
  public class SysUserOnlineController extends BaseController {
  
      /**
       * 获取在线用户监控列表
       *
       * @param ipaddr   IP地址
       * @param userName 用户名
       */
      @SaCheckPermission("monitor:online:list")
      @GetMapping("/list")
      public TableDataInfo<SysUserOnline> list(String ipaddr, String userName) {
          // 获取所有未过期的 token
          List<String> keys = StpUtil.searchTokenValue("", 0, -1, false);
          List<UserOnlineDTO> userOnlineDTOList = new ArrayList<>();
          for (String key : keys) {
              String token = StringUtils.substringAfterLast(key, ":");
              // 如果已经过期则跳过
              if (StpUtil.stpLogic.getTokenActivityTimeoutByToken(token) < -1) {
                  continue;
              }
              userOnlineDTOList.add(RedisUtils.getCacheObject(CacheConstants.ONLINE_TOKEN_KEY + token));
          }
          if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName)) {
              userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline ->
                  StringUtils.equals(ipaddr, userOnline.getIpaddr()) &&
                      StringUtils.equals(userName, userOnline.getUserName())
              );
          } else if (StringUtils.isNotEmpty(ipaddr)) {
              userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline ->
                  StringUtils.equals(ipaddr, userOnline.getIpaddr())
              );
          } else if (StringUtils.isNotEmpty(userName)) {
              userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline ->
                  StringUtils.equals(userName, userOnline.getUserName())
              );
          }
          Collections.reverse(userOnlineDTOList);
          userOnlineDTOList.removeAll(Collections.singleton(null));
          List<SysUserOnline> userOnlineList = BeanUtil.copyToList(userOnlineDTOList, SysUserOnline.class);
          return TableDataInfo.build(userOnlineList);
      }
  
      /**
       * 强退用户
       *
       * @param tokenId token值
       */
      @SaCheckPermission("monitor:online:forceLogout")
      @Log(title = "在线用户", businessType = BusinessType.FORCE)
      @DeleteMapping("/{tokenId}")
      public R<Void> forceLogout(@PathVariable String tokenId) {
          try {
              StpUtil.kickoutByTokenValue(tokenId);
          } catch (NotLoginException ignored) {
          }
          return R.ok();
      }
  }
  