123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388 |
- using System.Security.Claims;
- using Fw.Utility.UnifyResponse;
- using Hotline.Caching.Interfaces;
- using Hotline.Caching.Services;
- using Hotline.Identity;
- using Hotline.Identity.Accounts;
- using Hotline.Orders;
- using Hotline.Push;
- using Hotline.Schedulings;
- using Hotline.SeedData;
- using Hotline.Settings;
- using Hotline.Share.Dtos.FlowEngine;
- using Hotline.Share.Dtos.Identity;
- using Hotline.Share.Dtos.Snapshot;
- using Hotline.Share.Enums.FlowEngine;
- using Hotline.Share.Enums.Identity;
- using Hotline.Share.Enums.Snapshot;
- using Hotline.Share.Enums.User;
- using Hotline.Share.Tools;
- using Hotline.Snapshot;
- using Hotline.Snapshot.Interfaces;
- using Hotline.Users;
- using IdentityModel;
- using Mapster;
- using Microsoft.AspNetCore.Identity;
- using Microsoft.Extensions.Options;
- using SqlSugar;
- using XF.Domain.Authentications;
- using XF.Domain.Cache;
- using XF.Domain.Dependency;
- using XF.Domain.Exceptions;
- using XF.Domain.Options;
- using XF.Domain.Repository;
- namespace Hotline.Application.Identity;
- public class IdentityAppService : IIdentityAppService, IScopeDependency
- {
- private readonly IAccountRepository _accountRepository;
- private readonly IRepository<Citizen> _citizenRepository;
- private readonly ISessionContext _sessionContext;
- private readonly IAccountDomainService _accountDomainService;
- private readonly IRepository<User> _userRepository;
- private readonly IJwtSecurity _jwtSecurity;
- private readonly IOptionsSnapshot<IdentityConfiguration> _identityOptionsAccessor;
- private readonly ITypedCache<AudienceTicket> _cacheAudience;
- private readonly IMessageCodeDomainService _messageCodeDomainService;
- private readonly IRepository<Scheduling> _schedulingRepository;
- private readonly IOrderDomainService _orderDomainService;
- private readonly ISystemSettingCacheManager _systemSettingCacheManager;
- private readonly IThirdIdentiyService _thirdIdentiyService;
- private readonly IThirdAccountRepository _thirdAccountRepository;
- private readonly IGuiderInfoRepository _guiderInfoRepository;
- private readonly IVolunteerRepository _volunteerRepository;
- private readonly ISystemLogRepository _systemLog;
- public IdentityAppService(
- IAccountRepository accountRepository,
- IAccountDomainService accountDomainService,
- IRepository<User> userRepository,
- IJwtSecurity jwtSecurity,
- IOptionsSnapshot<IdentityConfiguration> identityOptionsAccessor,
- ITypedCache<AudienceTicket> cacheAudience,
- IMessageCodeDomainService messageCodeDomainService,
- IRepository<Scheduling> schedulingRepository,
- IOrderDomainService orderDomainService,
- ISystemSettingCacheManager systemSettingCacheManager,
- IThirdIdentiyService thirdIdentiyService,
- IThirdAccountRepository thirdAccountRepository,
- ISessionContext sessionContext,
- IRepository<Citizen> citizenRepository,
- IGuiderInfoRepository guiderInfoRepository,
- IVolunteerRepository volunteerRepository,
- ISystemLogRepository systemLog)
- {
- _accountRepository = accountRepository;
- _accountDomainService = accountDomainService;
- _userRepository = userRepository;
- _jwtSecurity = jwtSecurity;
- _identityOptionsAccessor = identityOptionsAccessor;
- _cacheAudience = cacheAudience;
- _messageCodeDomainService = messageCodeDomainService;
- _schedulingRepository = schedulingRepository;
- _orderDomainService = orderDomainService;
- _systemSettingCacheManager = systemSettingCacheManager;
- _thirdIdentiyService = thirdIdentiyService;
- _thirdAccountRepository = thirdAccountRepository;
- _sessionContext = sessionContext;
- _citizenRepository = citizenRepository;
- _guiderInfoRepository = guiderInfoRepository;
- _volunteerRepository = volunteerRepository;
- _systemLog = systemLog;
- }
- public async Task<string> OldToNewLoginAsync(HotlineLoginOldToNewDto dto, CancellationToken cancellationToken)
- {
- var account = await _accountRepository.GetExtAsync(
- d => d.UserName == dto.UserName,
- d => d.Includes(x => x.Roles));
- if (account is null)
- throw UserFriendlyException.SameMessage("未找到帐号,请联系管理员查看");
- if (account.Status != EAccountStatus.Normal)
- throw UserFriendlyException.SameMessage("帐号已被注销,请联系管理员查看");
- if (account.LockoutEnabled && account.LockoutEnd >= DateTime.Now)
- throw UserFriendlyException.SameMessage("账号被锁定!请联系管理员查看");
- var userInfo = await _userRepository.GetAsync(p => p.Id == account.Id, cancellationToken);
- //限制系统类型账户频繁获取token的行为
- //todo
- if (account.LockoutEnd.HasValue || account.AccessFailedCount > 0)
- {
- account.LockoutEnd = null;
- account.AccessFailedCount = 0;
- await _accountRepository.UpdateAsync(account, cancellationToken);
- }
- var user = await _userRepository.Queryable()
- .Includes(d => d.Organization)
- .FirstAsync(d => d.Id == account.Id);
- //平均派单
- var averageSendOrder = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.AverageSendOrder).SettingValue[0]);
- if (averageSendOrder)
- {
- await AverageOrderScheduling(account.Id, cancellationToken);
- }
- var jwtOptions = _identityOptionsAccessor.Value.Jwt;
- var claims = new List<Claim>
- {
- //new(JwtClaimTypes.Id, account.Id),
- new(JwtClaimTypes.Subject, account.Id),
- new(JwtClaimTypes.PhoneNumber, account.PhoneNo ?? string.Empty),
- new(AppClaimTypes.UserDisplayName, account.Name),
- new(JwtClaimTypes.Scope, jwtOptions.Scope),
- new(AppClaimTypes.UserPasswordChanged, account.PasswordChanged.ToString()),
- new(AppClaimTypes.StaffNo, user.StaffNo ?? string.Empty),
- };
- if (!string.IsNullOrEmpty(user.OrgId) && user.Organization is not null)
- {
- claims.AddRange(
- new List<Claim>
- {
- new(AppClaimTypes.DepartmentId, user.OrgId ?? string.Empty),
- new(AppClaimTypes.DepartmentIsCenter, user.Organization?.IsCenter.ToString()),
- new(AppClaimTypes.DepartmentName, user.Organization?.Name ?? string.Empty),
- new(AppClaimTypes.DepartmentAreaCode, user.Organization?.AreaCode ?? string.Empty),
- new(AppClaimTypes.DepartmentAreaName, user.Organization?.AreaName ?? string.Empty),
- new(AppClaimTypes.DepartmentLevel, user.Organization?.Level.ToString() ?? string.Empty),
- new(AppClaimTypes.AreaId, user.OrgId?.GetHigherOrgId() ?? string.Empty),
- }
- );
- }
- claims.AddRange(account.Roles.Select(d => new Claim(JwtClaimTypes.Role, d.Name)));
- var audience = new AudienceTicket(account.Id);
- var expiredSeconds = jwtOptions.Expired <= 0 ? 3600 : jwtOptions.Expired;
- await _cacheAudience.SetAsync(audience.Id, audience, TimeSpan.FromSeconds(expiredSeconds), cancellationToken);
- var token = _jwtSecurity.EncodeJwtToken(claims, audience.Ticket);
- return token;
- }
- public async Task<(bool,bool, User)> IsCheckAdmin(string userName)
- {
- var isAdmin = false;
- var isCenter = false;
- var account = await _accountRepository.GetExtAsync(
- d => d.UserName == userName,
- d => d.Includes(x => x.Roles)
- );
- if (account is null)
- throw UserFriendlyException.SameMessage("未找到帐号,请联系管理员查看");
- if (account.Status != EAccountStatus.Normal)
- throw UserFriendlyException.SameMessage("帐号已被注销,请联系管理员查看");
- if (account.LockoutEnabled && account.LockoutEnd >= DateTime.Now)
- throw UserFriendlyException.SameMessage("账号被锁定!请联系管理员查看");
- var user = await _userRepository.Queryable()
- .Includes(d => d.Organization)
- .Includes(d => d.Roles)
- .FirstAsync(d => d.Id == account.Id);
- var systemAdministrator = _systemSettingCacheManager.GetSetting(SettingConstants.SystemAdministrator)?.SettingValue[0];
- if (!string.IsNullOrEmpty(systemAdministrator) && (user.Roles.Any(x=>x.Name.Contains(systemAdministrator)) || user.Roles.Any(x=>x.Name.Contains(RoleSeedData.AdminRole))))
- isAdmin = true;
- else
- isAdmin = false;
- isCenter = user.Organization.IsCenter;
- return (isAdmin, isCenter,user);
- }
- public async Task<string> LoginAsync(LoginDto dto, CancellationToken cancellationToken)
- {
- var account = await _accountRepository.GetExtAsync(
- d => d.UserName == dto.Username,
- d => d.Includes(x => x.Roles));
- if (account == null)
- throw new UserFriendlyException($"用户名或密码错误!{System.Text.Json.JsonSerializer.Serialize(dto)}", "用户名或密码错误!");
- var userInfo = await _userRepository.GetAsync(p => p.Id == account.Id, cancellationToken);
- //校验验证码
- await _messageCodeDomainService.CheckdCode(account.UserName, userInfo.PhoneNo, dto.MsgCode, cancellationToken);
- if (account.Status != EAccountStatus.Normal)
- throw UserFriendlyException.SameMessage("用户名或密码错误!");
- if (account.LockoutEnabled && account.LockoutEnd >= DateTime.Now)
- throw UserFriendlyException.SameMessage("账号被锁定!");
- var verifyResult = _accountDomainService.VerifyPassword(account, dto.Password);
- if (verifyResult == PasswordVerificationResult.Failed)
- {
- var lockoutOptions = _identityOptionsAccessor.Value.Lockout;
- account.AccessFailedCount += 1;
- if (account.LockoutEnabled && account.AccessFailedCount >= lockoutOptions.MaxFailedAccessAttempts)
- account.LockoutEnd = DateTime.Now.Add(lockoutOptions.DefaultLockoutTimeSpan);
- await _accountRepository.UpdateAsync(account, cancellationToken);
- throw new UserFriendlyException($"用户名或密码错误!{System.Text.Json.JsonSerializer.Serialize(dto)}", "用户名或密码错误!");
- }
- //限制系统类型账户频繁获取token的行为
- //todo
- if (account.LockoutEnd.HasValue || account.AccessFailedCount > 0)
- {
- account.LockoutEnd = null;
- account.AccessFailedCount = 0;
- await _accountRepository.UpdateAsync(account, cancellationToken);
- }
- var user = await _userRepository.Queryable()
- .Includes(d => d.Organization)
- .FirstAsync(d => d.Id == account.Id);
- if (user == null)
- throw UserFriendlyException.SameMessage("未查询到用户数据");
- //平均派单
- var averageSendOrder = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.AverageSendOrder).SettingValue[0]);
- if (averageSendOrder)
- {
- await AverageOrderScheduling(account.Id, cancellationToken);
- }
- var jwtOptions = _identityOptionsAccessor.Value.Jwt;
- var claims = new List<Claim>
- {
- //new(JwtClaimTypes.Id, account.Id),
- new(JwtClaimTypes.Subject, account.Id),
- new(JwtClaimTypes.PhoneNumber, account.PhoneNo ?? string.Empty),
- new(AppClaimTypes.UserDisplayName, account.Name),
- new(JwtClaimTypes.Scope, jwtOptions.Scope),
- new(AppClaimTypes.UserPasswordChanged, account.PasswordChanged.ToString()),
- new(AppClaimTypes.StaffNo, user.StaffNo ?? string.Empty),
- };
- if (!string.IsNullOrEmpty(user.OrgId) && user.Organization is not null)
- {
- claims.AddRange(
- new List<Claim>
- {
- new(AppClaimTypes.DepartmentId, user.OrgId ?? string.Empty),
- new(AppClaimTypes.DepartmentIsCenter, user.Organization?.IsCenter.ToString()),
- new(AppClaimTypes.DepartmentName, user.Organization?.Name ?? string.Empty),
- new(AppClaimTypes.DepartmentAreaCode, user.Organization?.AreaCode ?? string.Empty),
- new(AppClaimTypes.DepartmentAreaName, user.Organization?.AreaName ?? string.Empty),
- new(AppClaimTypes.DepartmentLevel, user.Organization?.Level.ToString() ?? string.Empty),
- new(AppClaimTypes.AreaId, user.OrgId?.GetHigherOrgId() ?? string.Empty),
- }
- );
- }
- claims.AddRange(account.Roles.Select(d => new Claim(JwtClaimTypes.Role, d.Name)));
- var audience = new AudienceTicket(account.Id);
- var expiredSeconds = jwtOptions.Expired <= 0 ? 3600 : jwtOptions.Expired;
- await _cacheAudience.SetAsync(audience.Id, audience, TimeSpan.FromSeconds(expiredSeconds), cancellationToken);
- var token = _jwtSecurity.EncodeJwtToken(claims, audience.Ticket);
- return token;
- }
- public async Task AverageOrderScheduling(string id, CancellationToken cancellationToken)
- {
- try
- {
- DateTime time = DateTime.Parse(DateTime.Now.ToString("yyyy-MM-dd"));
- //&& x.AtWork!.Value != true
- //根据当前时间获取排班信息
- var scheduling = await _schedulingRepository.Queryable()
- .Includes(x => x.SchedulingUser)
- .Where(x => x.SchedulingTime == time &&
- (x.AtWork == true || x.AtWork == null) &&
- x.SchedulingUser.UserId == id)
- .OrderBy(x => x.SendOrderNum).FirstAsync(cancellationToken);
- if (scheduling != null)
- {
- scheduling.AtWork = true;
- //执行登录平均派单
- await _orderDomainService.LogAverageOrder(id, scheduling, cancellationToken);
- }
- }
- catch
- {
- // ignored
- }
- }
- /// <summary>
- /// 获取三方令牌
- /// </summary>
- /// <param name="dto"></param>
- /// <returns></returns>
- /// <exception cref="UserFriendlyException"></exception>
- public async Task<TokenOutDto> GetThredTokenAsync(ThirdTokenInDto dto)
- {
- var thirdDto = dto.Adapt<ThirdTokenDto>();
- if (dto.ThirdType == EThirdType.WeChat)
- {
- thirdDto.AppId = _systemSettingCacheManager.WxOpenAppId;
- thirdDto.Secret = _systemSettingCacheManager.WxOpenAppSecret;
- }
- var thirdToken = await _thirdIdentiyService.GetTokenAsync(thirdDto);
- var phone = await _thirdIdentiyService.GetPhoneNumberAsync(thirdDto);
- var thirdAccount = await _thirdAccountRepository.GetByOpenIdAsync(thirdToken.OpenId);
- // 新用户注册
- if (thirdAccount is null)
- {
- thirdAccount = thirdToken.Adapt<ThirdAccount>();
- thirdAccount.CitizenType = EReadPackUserType.Citizen;
- thirdAccount.PhoneNumber = phone.PhoneNumber;
- var guider = await _guiderInfoRepository.GetByPhoneNumberAsync(phone.PhoneNumber);
- if (guider != null)
- {
- thirdAccount.CitizenType = EReadPackUserType.Guider;
- thirdAccount.UserId = guider.Id;
- }
- else
- {
- var citizen = await _citizenRepository.Queryable().Where(m => m.PhoneNumber == phone.PhoneNumber).FirstAsync();
- thirdAccount.UserId = citizen?.Id;
- }
- thirdAccount.Id = await _thirdAccountRepository.AddAsync(thirdAccount);
- }
- return await GetJwtToken(thirdAccount);
- }
- public async Task<TokenOutDto> RefreshTokenAsync(string openId)
- {
- var thirdAccount = await _thirdAccountRepository.GetByOpenIdAsync(openId)
- ?? throw UserFriendlyException.SameMessage("未找到用户信息");
- return await GetJwtToken(thirdAccount);
- }
- private async Task<TokenOutDto> GetJwtToken(ThirdAccount thirdAccount)
- {
- var jwtOptions = _identityOptionsAccessor.Value.Jwt;
- var claims = new List<Claim>
- {
- new(JwtClaimTypes.Subject, thirdAccount.Id),
- new(JwtClaimTypes.PhoneNumber, thirdAccount.PhoneNumber ?? string.Empty),
- new(JwtClaimTypes.Scope, jwtOptions.Scope),
- new(AppClaimTypes.OpenId, thirdAccount.OpenId),
- };
- var audience = new AudienceTicket(thirdAccount.Id);
- var expiredSeconds = jwtOptions.Expired <= 0 ? 3600 : jwtOptions.Expired;
- await _cacheAudience.SetAsync(audience.Id, audience, TimeSpan.FromSeconds(expiredSeconds));
- var token = _jwtSecurity.EncodeJwtToken(claims, audience.Ticket);
- var isVolunteer = await _volunteerRepository.IsVolunteerAsync(thirdAccount.PhoneNumber);
- return new TokenOutDto()
- {
- UserType = thirdAccount.CitizenType,
- Token = token,
- IsVolunteer = isVolunteer,
- OpenId = thirdAccount.OpenId,
- PhoneNumber = thirdAccount.PhoneNumber,
- InvitationCode = thirdAccount.InvitationCode,
- UserName = thirdAccount.UserName
- };
- }
- }
|