平安校园
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

203 lines
8.3 KiB

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