平安校园
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

SessionService.cs 8.3 KiB

před 4 měsíci
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. 
  2. //
  3. using Masuit.Tools.DateTimeExt;
  4. namespace SafeCampus.System;
  5. /// <summary>
  6. /// <inheritdoc cref="ISessionService"/>
  7. /// </summary>
  8. public class SessionService : DbRepository<SysUser>, ISessionService
  9. {
  10. private readonly ISimpleCacheService _simpleCacheService;
  11. private readonly IEventPublisher _eventPublisher;
  12. public SessionService(ISimpleCacheService simpleCacheService, IEventPublisher eventPublisher)
  13. {
  14. _simpleCacheService = simpleCacheService;
  15. _eventPublisher = eventPublisher;
  16. }
  17. /// <inheritdoc/>
  18. public async Task<SqlSugarPagedList<SessionOutput>> PageB(SessionPageInput input)
  19. {
  20. //获取b端token列表
  21. var bTokenInfoDic = GetTokenDicFromRedis();
  22. //获取用户ID列表
  23. var userIds = bTokenInfoDic.Keys.Select(it => it.ToLong()).ToList();
  24. var query = Context.Queryable<SysUser>().Where(it => userIds.Contains(it.Id))//根据ID查询
  25. .WhereIF(!string.IsNullOrEmpty(input.Name), it => it.Name.Contains(input.Name))//根据姓名查询
  26. .WhereIF(!string.IsNullOrEmpty(input.Account), it => it.Account.Contains(input.Account))//根据账号查询
  27. .WhereIF(!string.IsNullOrEmpty(input.LatestLoginIp), it => it.LatestLoginIp.Contains(input.LatestLoginIp))//根据IP查询
  28. .OrderBy(it => it.LatestLoginTime, OrderByType.Desc).Select<SessionOutput>().Mapper(it =>
  29. {
  30. var tokenInfos = bTokenInfoDic[it.Id.ToString()];//获取用户token信息
  31. GetTokenInfos(ref tokenInfos, LoginClientTypeEnum.B);//获取剩余时间
  32. it.TokenCount = tokenInfos.Count;//令牌数量
  33. it.TokenSignList = tokenInfos;//令牌列表
  34. //如果有mqtt客户端ID就是在线
  35. it.OnlineStatus = tokenInfos.Any(it => it.ClientIds.Count > 0)
  36. ? SysDictConst.ONLINE_STATUS_ONLINE
  37. : SysDictConst.ONLINE_STATUS_OFFLINE;
  38. });
  39. var pageInfo = await query.ToPagedListAsync(input.PageNum, input.PageSize);//分页
  40. pageInfo.List.OrderByDescending(it => it.TokenCount);
  41. return pageInfo;
  42. }
  43. /// <inheritdoc/>
  44. public async Task<SqlSugarPagedList<SessionOutput>> PageC(SessionPageInput input)
  45. {
  46. return new SqlSugarPagedList<SessionOutput> { PageNum = 1, PageSize = 20, Total = 0, Pages = 1, HasNextPages = false };
  47. }
  48. /// <inheritdoc/>
  49. public SessionAnalysisOutPut Analysis()
  50. {
  51. var tokenDic = GetTokenDicFromRedis();//redisToken会话字典信息
  52. var tokenInfosList = tokenDic.Values.ToList();//端token列表
  53. var dicB = new Dictionary<string, List<TokenInfo>>();
  54. var dicC = new Dictionary<string, List<TokenInfo>>();
  55. var onLineCount = 0;
  56. foreach (var token in tokenDic)
  57. {
  58. var b = token.Value.Where(it => it.LoginClientType == LoginClientTypeEnum.B).ToList();//获取该用户B端token
  59. var c = token.Value.Where(it => it.LoginClientType == LoginClientTypeEnum.C).ToList();//获取该用户C端token
  60. if (b.Count > 0)
  61. dicB.Add(token.Key, b);
  62. if (c.Count > 0)
  63. dicC.Add(token.Key, c);
  64. var count = token.Value.Count(it => it.ClientIds.Count > 0);//计算在线用户
  65. onLineCount += count;
  66. }
  67. var tokenB = dicB.Values.ToList();//b端token列表
  68. var tokenC = dicC.Values.ToList();//c端token列表
  69. int maxCountB = 0, maxCountC = 0;
  70. if (tokenB.Count > 0)
  71. maxCountB = tokenB.OrderByDescending(it => it.Count).Take(1).First().Count;//b端最大会话数
  72. if (tokenC.Count > 0)
  73. maxCountC = tokenC.OrderByDescending(it => it.Count).Take(1).First().Count;//C端最大会话数
  74. return new SessionAnalysisOutPut
  75. {
  76. OnLineCount = onLineCount,
  77. CurrentSessionTotalCount = tokenB.Count + tokenC.Count,
  78. MaxTokenCount = maxCountB > maxCountC ? maxCountB : maxCountC,
  79. ProportionOfBAndC = $"{tokenB.Count}/{tokenC.Count}"
  80. };
  81. }
  82. /// <inheritdoc/>
  83. public async Task ExitSession(BaseIdInput input)
  84. {
  85. var userId = input.Id.ToString();
  86. //token列表
  87. var tokenInfos = _simpleCacheService.HashGetOne<List<TokenInfo>>(CacheConst.CACHE_USER_TOKEN, userId);
  88. //从列表中删除
  89. _simpleCacheService.HashDel<List<TokenInfo>>(CacheConst.CACHE_USER_TOKEN, userId);
  90. await NoticeUserLoginOut(userId, tokenInfos);
  91. }
  92. /// <inheritdoc/>
  93. public async Task ExitToken(ExitTokenInput input)
  94. {
  95. var userId = input.Id.ToString();
  96. //获取该用户的token信息
  97. var tokenInfos = _simpleCacheService.HashGetOne<List<TokenInfo>>(CacheConst.CACHE_USER_TOKEN, userId);
  98. //当前需要踢掉用户的token
  99. var deleteTokens = tokenInfos.Where(it => input.Tokens.Contains(it.Token)).ToList();
  100. //踢掉包含token列表的token信息
  101. tokenInfos = tokenInfos.Where(it => !input.Tokens.Contains(it.Token)).ToList();
  102. if (tokenInfos.Count > 0)
  103. _simpleCacheService.HashAdd(CacheConst.CACHE_USER_TOKEN, userId, tokenInfos);//如果还有token则更新token
  104. else
  105. _simpleCacheService.HashDel<List<TokenInfo>>(CacheConst.CACHE_USER_TOKEN, userId);//否则直接删除key
  106. await NoticeUserLoginOut(userId, deleteTokens);
  107. }
  108. #region 方法
  109. /// <summary>
  110. /// 获取redis中token信息列表
  111. /// </summary>
  112. /// <returns></returns>
  113. public Dictionary<string, List<TokenInfo>> GetTokenDicFromRedis()
  114. {
  115. var clockSkew = App.GetConfig<int>("JWTSettings:ClockSkew");//获取过期时间容错值(秒)
  116. //redis获取token信息hash集合,并转成字典
  117. var bTokenDic = _simpleCacheService.HashGetAll<List<TokenInfo>>(CacheConst.CACHE_USER_TOKEN).ToDictionary(u => u.Key, u => u.Value);
  118. if (bTokenDic != null)
  119. {
  120. bTokenDic.ForEach(it =>
  121. {
  122. var tokens = it.Value.Where(it => it.TokenTimeout.AddSeconds(clockSkew) > DateTime.Now).ToList();//去掉登录超时的
  123. if (tokens.Count == 0)
  124. {
  125. //表示都过期了
  126. bTokenDic.Remove(it.Key);
  127. }
  128. else
  129. {
  130. bTokenDic[it.Key] = tokens;//重新赋值token
  131. }
  132. });
  133. if (bTokenDic.Count > 0)
  134. {
  135. _simpleCacheService.HashSet(CacheConst.CACHE_USER_TOKEN, bTokenDic);//如果还有token则更新token
  136. }
  137. else
  138. {
  139. _simpleCacheService.Remove(CacheConst.CACHE_USER_TOKEN);//否则直接删除key
  140. }
  141. return bTokenDic;
  142. }
  143. return new Dictionary<string, List<TokenInfo>>();
  144. }
  145. /// <summary>
  146. /// 获取token剩余时间信息
  147. /// </summary>
  148. /// <param name="tokenInfos">token列表</param>
  149. /// <param name="loginClientType">登录类型</param>
  150. public void GetTokenInfos(ref List<TokenInfo> tokenInfos, LoginClientTypeEnum loginClientType)
  151. {
  152. tokenInfos = tokenInfos.Where(it => it.LoginClientType == loginClientType).ToList();
  153. tokenInfos.ForEach(it =>
  154. {
  155. var now = DateTime.Now;
  156. it.TokenRemain = now.GetDiffTime(it.TokenTimeout);//获取时间差
  157. var tokenSecond = it.TokenTimeout.AddMinutes(-it.Expire).ConvertDateTimeToLong();//颁发时间转为时间戳
  158. var timeoutSecond = it.TokenTimeout.ConvertDateTimeToLong();//过期时间转为时间戳
  159. var tokenRemainPercent =
  160. 1 - (now.ConvertDateTimeToLong() - tokenSecond) * 1.0 / (timeoutSecond - tokenSecond);//求百分比,用现在时间-token颁布时间除以超时时间-token颁布时间
  161. it.TokenRemainPercent = tokenRemainPercent;
  162. });
  163. }
  164. /// <summary>
  165. /// 通知用户下线
  166. /// </summary>
  167. /// <returns></returns>
  168. private async Task NoticeUserLoginOut(string userId, List<TokenInfo> tokenInfos)
  169. {
  170. await _eventPublisher.PublishAsync(EventSubscriberConst.USER_LOGIN_OUT, new UserLoginOutEvent
  171. {
  172. Message = "您已被强制下线!",
  173. TokenInfos = tokenInfos,
  174. UserId = userId
  175. });//通知用户下线
  176. }
  177. #endregion 方法
  178. }