IdentityAppService.cs 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. using System.Security.Claims;
  2. using Hotline.Identity;
  3. using Hotline.Identity.Accounts;
  4. using Hotline.Orders;
  5. using Hotline.Push;
  6. using Hotline.Schedulings;
  7. using Hotline.Settings;
  8. using Hotline.Share.Dtos.Identity;
  9. using Hotline.Share.Enums.Identity;
  10. using Hotline.Users;
  11. using IdentityModel;
  12. using Microsoft.AspNetCore.Identity;
  13. using Microsoft.Extensions.Options;
  14. using XF.Domain.Authentications;
  15. using XF.Domain.Cache;
  16. using XF.Domain.Dependency;
  17. using XF.Domain.Exceptions;
  18. using XF.Domain.Options;
  19. using XF.Domain.Repository;
  20. namespace Hotline.Application.Identity;
  21. public class IdentityAppService : IIdentityAppService, IScopeDependency
  22. {
  23. private readonly IAccountRepository _accountRepository;
  24. private readonly IAccountDomainService _accountDomainService;
  25. private readonly IRepository<User> _userRepository;
  26. private readonly IJwtSecurity _jwtSecurity;
  27. private readonly IOptionsSnapshot<IdentityConfiguration> _identityOptionsAccessor;
  28. private readonly ITypedCache<AudienceTicket> _cacheAudience;
  29. private readonly IMessageCodeDomainService _messageCodeDomainService;
  30. private readonly IRepository<Scheduling> _schedulingRepository;
  31. private readonly IOrderDomainService _orderDomainService;
  32. public IdentityAppService(
  33. IAccountRepository accountRepository,
  34. IAccountDomainService accountDomainService,
  35. IRepository<User> userRepository,
  36. IJwtSecurity jwtSecurity,
  37. IOptionsSnapshot<IdentityConfiguration> identityOptionsAccessor,
  38. ITypedCache<AudienceTicket> cacheAudience,
  39. IMessageCodeDomainService messageCodeDomainService,
  40. IRepository<Scheduling> schedulingRepository,
  41. IOrderDomainService orderDomainService)
  42. {
  43. _accountRepository = accountRepository;
  44. _accountDomainService = accountDomainService;
  45. _userRepository = userRepository;
  46. _jwtSecurity = jwtSecurity;
  47. _identityOptionsAccessor = identityOptionsAccessor;
  48. _cacheAudience = cacheAudience;
  49. _messageCodeDomainService = messageCodeDomainService;
  50. _schedulingRepository = schedulingRepository;
  51. _orderDomainService = orderDomainService;
  52. }
  53. public async Task<string> LoginAsync(LoginDto dto, CancellationToken cancellationToken)
  54. {
  55. var account = await _accountRepository.GetExtAsync(
  56. d => d.UserName == dto.Username,
  57. d => d.Includes(x => x.Roles));
  58. if (account == null)
  59. throw new UserFriendlyException($"用户名或密码错误!{System.Text.Json.JsonSerializer.Serialize(dto)}", "用户名或密码错误!");
  60. var userInfo = await _userRepository.GetAsync(p => p.Id == account.Id, cancellationToken);
  61. //校验验证码
  62. await _messageCodeDomainService.CheckdCode(account.UserName, userInfo.PhoneNo, dto.MsgCode, cancellationToken);
  63. if (account.Status != EAccountStatus.Normal)
  64. throw UserFriendlyException.SameMessage("用户名或密码错误!");
  65. if (account.LockoutEnabled && account.LockoutEnd >= DateTime.Now)
  66. throw UserFriendlyException.SameMessage("账号被锁定!");
  67. var verifyResult = _accountDomainService.VerifyPassword(account, dto.Password);
  68. if (verifyResult == PasswordVerificationResult.Failed)
  69. {
  70. var lockoutOptions = _identityOptionsAccessor.Value.Lockout;
  71. account.AccessFailedCount += 1;
  72. if (account.LockoutEnabled && account.AccessFailedCount >= lockoutOptions.MaxFailedAccessAttempts)
  73. account.LockoutEnd = DateTime.Now.Add(lockoutOptions.DefaultLockoutTimeSpan);
  74. await _accountRepository.UpdateAsync(account, cancellationToken);
  75. throw new UserFriendlyException($"用户名或密码错误!{System.Text.Json.JsonSerializer.Serialize(dto)}", "用户名或密码错误!");
  76. }
  77. //限制系统类型账户频繁获取token的行为
  78. //todo
  79. if (account.LockoutEnd.HasValue || account.AccessFailedCount > 0)
  80. {
  81. account.LockoutEnd = null;
  82. account.AccessFailedCount = 0;
  83. await _accountRepository.UpdateAsync(account, cancellationToken);
  84. }
  85. var user = await _userRepository.Queryable()
  86. .Includes(d => d.Organization)
  87. .FirstAsync(d => d.Id == account.Id);
  88. if (user == null)
  89. throw UserFriendlyException.SameMessage("未查询到用户数据");
  90. //平均派单
  91. await AverageOrderScheduling(account.Id, cancellationToken);
  92. var jwtOptions = _identityOptionsAccessor.Value.Jwt;
  93. var claims = new List<Claim>
  94. {
  95. //new(JwtClaimTypes.Id, account.Id),
  96. new(JwtClaimTypes.Subject, account.Id),
  97. new(JwtClaimTypes.PhoneNumber, account.PhoneNo ?? string.Empty),
  98. new(AppClaimTypes.UserDisplayName, account.Name),
  99. new(JwtClaimTypes.Scope, jwtOptions.Scope),
  100. new(AppClaimTypes.UserPasswordChanged, account.PasswordChanged.ToString()),
  101. new(AppClaimTypes.StaffNo, user.StaffNo ?? string.Empty),
  102. };
  103. if (!string.IsNullOrEmpty(user.OrgId) && user.Organization is not null)
  104. {
  105. claims.AddRange(
  106. new List<Claim>
  107. {
  108. new(AppClaimTypes.DepartmentId, user.OrgId ?? string.Empty),
  109. new(AppClaimTypes.DepartmentIsCenter, user.Organization?.IsCenter.ToString()),
  110. new(AppClaimTypes.DepartmentName, user.Organization?.Name ?? string.Empty),
  111. new(AppClaimTypes.DepartmentAreaCode, user.Organization?.AreaCode ?? string.Empty),
  112. new(AppClaimTypes.DepartmentAreaName, user.Organization?.AreaName ?? string.Empty),
  113. new(AppClaimTypes.DepartmentLevel, user.Organization?.Level.ToString() ?? string.Empty),
  114. new(AppClaimTypes.AreaId, user.OrgId?.GetHigherOrgId() ?? string.Empty),
  115. }
  116. );
  117. }
  118. claims.AddRange(account.Roles.Select(d => new Claim(JwtClaimTypes.Role, d.Name)));
  119. var audience = new AudienceTicket(account.Id);
  120. var expiredSeconds = jwtOptions.Expired <= 0 ? 3600 : jwtOptions.Expired;
  121. await _cacheAudience.SetAsync(audience.Id, audience, TimeSpan.FromSeconds(expiredSeconds), cancellationToken);
  122. var token = _jwtSecurity.EncodeJwtToken(claims, audience.Ticket);
  123. return token;
  124. }
  125. public async Task AverageOrderScheduling(string id, CancellationToken cancellationToken)
  126. {
  127. try
  128. {
  129. DateTime time = DateTime.Parse(DateTime.Now.ToString("yyyy-MM-dd"));
  130. //&& x.AtWork!.Value != true
  131. //根据当前时间获取排班信息
  132. var scheduling = await _schedulingRepository.Queryable()
  133. .Includes(x => x.SchedulingUser)
  134. .Where(x => x.SchedulingTime == time &&
  135. x.WorkingTime <= DateTime.Now.TimeOfDay &&
  136. x.OffDutyTime >= DateTime.Now.TimeOfDay &&
  137. (x.AtWork == true || x.AtWork == null) &&
  138. x.SchedulingUser.UserId == id)
  139. .OrderBy(x => x.SendOrderNum).FirstAsync(cancellationToken);
  140. if (scheduling != null)
  141. {
  142. scheduling.AtWork = true;
  143. await _schedulingRepository.UpdateAsync(scheduling, cancellationToken);
  144. //执行登录平均派单
  145. await _orderDomainService.LogAverageOrder(id, cancellationToken);
  146. }
  147. }
  148. catch
  149. {
  150. // ignored
  151. }
  152. }
  153. }