|
-
- //
-
-
-
-
-
-
-
-
- using Masuit.Tools.DateTimeExt;
-
- namespace SafeCampus.System;
-
- /// <summary>
- /// <inheritdoc cref="ISessionService"/>
- /// </summary>
- public class SessionService : DbRepository<SysUser>, ISessionService
- {
- private readonly ISimpleCacheService _simpleCacheService;
- private readonly IEventPublisher _eventPublisher;
-
- public SessionService(ISimpleCacheService simpleCacheService, IEventPublisher eventPublisher)
- {
- _simpleCacheService = simpleCacheService;
- _eventPublisher = eventPublisher;
- }
-
- /// <inheritdoc/>
- public async Task<SqlSugarPagedList<SessionOutput>> PageB(SessionPageInput input)
- {
- //获取b端token列表
- var bTokenInfoDic = GetTokenDicFromRedis();
-
- //获取用户ID列表
- var userIds = bTokenInfoDic.Keys.Select(it => it.ToLong()).ToList();
- var query = Context.Queryable<SysUser>().Where(it => userIds.Contains(it.Id))//根据ID查询
- .WhereIF(!string.IsNullOrEmpty(input.Name), it => it.Name.Contains(input.Name))//根据姓名查询
- .WhereIF(!string.IsNullOrEmpty(input.Account), it => it.Account.Contains(input.Account))//根据账号查询
- .WhereIF(!string.IsNullOrEmpty(input.LatestLoginIp), it => it.LatestLoginIp.Contains(input.LatestLoginIp))//根据IP查询
- .OrderBy(it => it.LatestLoginTime, OrderByType.Desc).Select<SessionOutput>().Mapper(it =>
- {
- var tokenInfos = bTokenInfoDic[it.Id.ToString()];//获取用户token信息
- GetTokenInfos(ref tokenInfos, LoginClientTypeEnum.B);//获取剩余时间
- it.TokenCount = tokenInfos.Count;//令牌数量
- it.TokenSignList = tokenInfos;//令牌列表
- //如果有mqtt客户端ID就是在线
- it.OnlineStatus = tokenInfos.Any(it => it.ClientIds.Count > 0)
- ? SysDictConst.ONLINE_STATUS_ONLINE
- : SysDictConst.ONLINE_STATUS_OFFLINE;
- });
-
- var pageInfo = await query.ToPagedListAsync(input.PageNum, input.PageSize);//分页
- pageInfo.List.OrderByDescending(it => it.TokenCount);
- return pageInfo;
- }
-
- /// <inheritdoc/>
- public async Task<SqlSugarPagedList<SessionOutput>> PageC(SessionPageInput input)
- {
- return new SqlSugarPagedList<SessionOutput> { PageNum = 1, PageSize = 20, Total = 0, Pages = 1, HasNextPages = false };
- }
-
- /// <inheritdoc/>
- public SessionAnalysisOutPut Analysis()
- {
- var tokenDic = GetTokenDicFromRedis();//redisToken会话字典信息
- var tokenInfosList = tokenDic.Values.ToList();//端token列表
- var dicB = new Dictionary<string, List<TokenInfo>>();
- var dicC = new Dictionary<string, List<TokenInfo>>();
- var onLineCount = 0;
- foreach (var token in tokenDic)
- {
- var b = token.Value.Where(it => it.LoginClientType == LoginClientTypeEnum.B).ToList();//获取该用户B端token
- var c = token.Value.Where(it => it.LoginClientType == LoginClientTypeEnum.C).ToList();//获取该用户C端token
- if (b.Count > 0)
- dicB.Add(token.Key, b);
- if (c.Count > 0)
- dicC.Add(token.Key, c);
- var count = token.Value.Count(it => it.ClientIds.Count > 0);//计算在线用户
- onLineCount += count;
- }
- var tokenB = dicB.Values.ToList();//b端token列表
- var tokenC = dicC.Values.ToList();//c端token列表
- int maxCountB = 0, maxCountC = 0;
- if (tokenB.Count > 0)
- maxCountB = tokenB.OrderByDescending(it => it.Count).Take(1).First().Count;//b端最大会话数
-
- if (tokenC.Count > 0)
- maxCountC = tokenC.OrderByDescending(it => it.Count).Take(1).First().Count;//C端最大会话数
- return new SessionAnalysisOutPut
- {
- OnLineCount = onLineCount,
- CurrentSessionTotalCount = tokenB.Count + tokenC.Count,
- MaxTokenCount = maxCountB > maxCountC ? maxCountB : maxCountC,
- ProportionOfBAndC = $"{tokenB.Count}/{tokenC.Count}"
- };
- }
-
- /// <inheritdoc/>
- public async Task ExitSession(BaseIdInput input)
- {
- var userId = input.Id.ToString();
- //token列表
- var tokenInfos = _simpleCacheService.HashGetOne<List<TokenInfo>>(CacheConst.CACHE_USER_TOKEN, userId);
- //从列表中删除
- _simpleCacheService.HashDel<List<TokenInfo>>(CacheConst.CACHE_USER_TOKEN, userId);
- await NoticeUserLoginOut(userId, tokenInfos);
- }
-
- /// <inheritdoc/>
- public async Task ExitToken(ExitTokenInput input)
- {
- var userId = input.Id.ToString();
- //获取该用户的token信息
- var tokenInfos = _simpleCacheService.HashGetOne<List<TokenInfo>>(CacheConst.CACHE_USER_TOKEN, userId);
- //当前需要踢掉用户的token
- var deleteTokens = tokenInfos.Where(it => input.Tokens.Contains(it.Token)).ToList();
- //踢掉包含token列表的token信息
- tokenInfos = tokenInfos.Where(it => !input.Tokens.Contains(it.Token)).ToList();
- if (tokenInfos.Count > 0)
- _simpleCacheService.HashAdd(CacheConst.CACHE_USER_TOKEN, userId, tokenInfos);//如果还有token则更新token
- else
- _simpleCacheService.HashDel<List<TokenInfo>>(CacheConst.CACHE_USER_TOKEN, userId);//否则直接删除key
- await NoticeUserLoginOut(userId, deleteTokens);
- }
-
- #region 方法
-
- /// <summary>
- /// 获取redis中token信息列表
- /// </summary>
- /// <returns></returns>
- public Dictionary<string, List<TokenInfo>> GetTokenDicFromRedis()
- {
- var clockSkew = App.GetConfig<int>("JWTSettings:ClockSkew");//获取过期时间容错值(秒)
- //redis获取token信息hash集合,并转成字典
- var bTokenDic = _simpleCacheService.HashGetAll<List<TokenInfo>>(CacheConst.CACHE_USER_TOKEN).ToDictionary(u => u.Key, u => u.Value);
- if (bTokenDic != null)
- {
- bTokenDic.ForEach(it =>
- {
- var tokens = it.Value.Where(it => it.TokenTimeout.AddSeconds(clockSkew) > DateTime.Now).ToList();//去掉登录超时的
- if (tokens.Count == 0)
- {
- //表示都过期了
- bTokenDic.Remove(it.Key);
- }
- else
- {
- bTokenDic[it.Key] = tokens;//重新赋值token
- }
- });
- if (bTokenDic.Count > 0)
- {
- _simpleCacheService.HashSet(CacheConst.CACHE_USER_TOKEN, bTokenDic);//如果还有token则更新token
- }
- else
- {
- _simpleCacheService.Remove(CacheConst.CACHE_USER_TOKEN);//否则直接删除key
- }
- return bTokenDic;
- }
- return new Dictionary<string, List<TokenInfo>>();
- }
-
- /// <summary>
- /// 获取token剩余时间信息
- /// </summary>
- /// <param name="tokenInfos">token列表</param>
- /// <param name="loginClientType">登录类型</param>
- public void GetTokenInfos(ref List<TokenInfo> tokenInfos, LoginClientTypeEnum loginClientType)
- {
- tokenInfos = tokenInfos.Where(it => it.LoginClientType == loginClientType).ToList();
- tokenInfos.ForEach(it =>
- {
- var now = DateTime.Now;
- it.TokenRemain = now.GetDiffTime(it.TokenTimeout);//获取时间差
- var tokenSecond = it.TokenTimeout.AddMinutes(-it.Expire).ConvertDateTimeToLong();//颁发时间转为时间戳
- var timeoutSecond = it.TokenTimeout.ConvertDateTimeToLong();//过期时间转为时间戳
- var tokenRemainPercent =
- 1 - (now.ConvertDateTimeToLong() - tokenSecond) * 1.0 / (timeoutSecond - tokenSecond);//求百分比,用现在时间-token颁布时间除以超时时间-token颁布时间
- it.TokenRemainPercent = tokenRemainPercent;
- });
- }
-
- /// <summary>
- /// 通知用户下线
- /// </summary>
- /// <returns></returns>
- private async Task NoticeUserLoginOut(string userId, List<TokenInfo> tokenInfos)
- {
- await _eventPublisher.PublishAsync(EventSubscriberConst.USER_LOGIN_OUT, new UserLoginOutEvent
- {
- Message = "您已被强制下线!",
- TokenInfos = tokenInfos,
- UserId = userId
- });//通知用户下线
- }
-
- #endregion 方法
- }
|