using Furion; using System; using GDZZ.Core; using GDZZ.Core.OAuth; using Furion.EventBus; using GDZZ.Core.Entity; using Furion.DataEncryption; using GDZZ.Application.Entity; using Furion.FriendlyException; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using System.Collections.Generic; using Microsoft.Extensions.Options; using Furion.DependencyInjection; using Furion.DynamicApiController; using Microsoft.AspNetCore.Http; using GDZZ.Application.Help; using Microsoft.AspNetCore.Authorization; using Mapster; using GDZZ.Core.Service; using TencentCloud.Sms.V20210111.Models; using TencentCloud.Common; using Polly; using System.IO; using System.Diagnostics.Eventing.Reader; namespace GDZZ.Application.Service.Auth { [ApiDescriptionSettings("Application", Name = "Auth", Order = 1)] public class AuthService : IAuthService, IDynamicApiController, ITransient { #region 仓储 private readonly SqlSugarRepository Baseuser; // wx用户仓储 private readonly SqlSugarRepository _sysUserRep; // 用户表仓储 private readonly SqlSugarRepository _sysTenantRep; //租户仓储 private readonly SqlSugarRepository Self; //职业仓储 private readonly SqlSugarRepository CompanyRep; private readonly SqlSugarRepository payTakeRep; //支付仓储 private readonly SqlSugarRepository _sysConfigRep; // 参数配置表仓储 private readonly UploadFileOptions _options; #endregion #region 服务 private readonly ISysCacheService _sysCacheService; //缓存 private readonly ICacheService cacheService; private readonly WechatOAuth _wechatOAuth; //微信权限服务 private readonly IHttpContextAccessor _httpContextAccessor; //http服务 private readonly IEventPublisher _eventPublisher; //事件写入服务 private readonly SqlSugarRepository invitaitionRey; private readonly SqlSugarRepository rechargeRep; //充值仓储 #endregion /// /// 获取配置文件 /// private readonly ThirdParty _oauthConfig; public AuthService( IOptions options, SqlSugarRepository Baseuser, SqlSugarRepository sysTenantRep, SqlSugarRepository sysUserRep, SqlSugarRepository Self, SqlSugarRepository CompanyRep, SqlSugarRepository payTakeRep, SqlSugarRepository sysConfigRep, SqlSugarRepository invitaitionRey, SqlSugarRepository rechargeRep, IOptions UFoptions, ISysCacheService sysCacheService, ICacheService cacheService, WechatOAuth wechatOAuth, IEventPublisher eventPublisher, IHttpContextAccessor httpContextAccessor) { this.CompanyRep= CompanyRep; this._eventPublisher= eventPublisher; this._sysUserRep = sysUserRep; this._sysTenantRep = sysTenantRep; this.Baseuser = Baseuser; this._httpContextAccessor = httpContextAccessor; this.cacheService = cacheService; this.Self = Self; this.payTakeRep = payTakeRep; this._sysCacheService= sysCacheService; this._sysConfigRep= sysConfigRep; this.invitaitionRey = invitaitionRey; this.rechargeRep= rechargeRep; _wechatOAuth = wechatOAuth; _oauthConfig = options.Value.Wechat; this._options = UFoptions.Value; } /// /// 手机端登录(一键登录) /// /// [HttpPost("/Mini/SignIn")] [AllowAnonymous] public async Task SignInAsync(PhoneModel phoneModel) { AuthUserOut authUserOut = new AuthUserOut(); Company company = new Company(); //读取凭证 var tokenModel = await this._wechatOAuth.GetCode2SessionAsync(phoneModel.Code); //解析电话 var phoneInfo = MiniProgramUtil.AESDecrypt(phoneModel.EncryptedDataStr, tokenModel.SessionKey, phoneModel.Iv); //查询系统用户 var sysUser = this._sysUserRep.AsQueryable() .Filter("TenantId", true) .First(x => x.Phone == phoneInfo.PhoneNumber); var wxUser = await this.Baseuser.AsQueryable() .Filter("TenantId", true) .Where(x => x.OpenID == tokenModel.OpenId).SingleAsync(); //账号不存在 生成系统账号 if (sysUser.IsEmpty()) { sysUser = await this._sysUserRep.InsertReturnEntityAsync(new SysUser() { Account = phoneInfo.PurePhoneNumber, AdminType = AdminType.None, Avatar = "https://gdzongzhi.com/assets/img/logo.png", Birthday = DateTime.Now, CreatedTime = DateTime.Now, CreatedUserId = null, CreatedUserName = null, Sex = Gender.UNKNOWN, Status = CommonStatus.ENABLE, Email = null, IsDeleted = false, Name = phoneInfo.PhoneNumber, Password = MD5Encryption.Encrypt("123456"), TenantId = 392820661919813, Phone = phoneInfo.PhoneNumber, NickName = "", Tel = null, }); } //区分不同类型账号 if (phoneModel.LogInType ==(int)UserEnum.JOB) { if (wxUser.IsEmpty()) { wxUser = await this.Baseuser.InsertReturnEntityAsync(new BaseUser() { UnionId = tokenModel.Unionid, CreatedUserId = sysUser.Id, CreatedTime = DateTime.Now, CreatedUserName = sysUser.Name, AvatarUrl = "https://gdzongzhi.com/assets/img/logo.png", Status =(int)CommonStatus.ENABLE, OpenID = tokenModel.OpenId, TenantId = 392820661919813, Type = (int)UserEnum.JOB, UserName = phoneInfo.PhoneNumber, }); } else { wxUser.Type = (int)UserEnum.JOB; var bsUser = await this.Baseuser.UpdateAsync(wxUser); } } else { //如果是企业账号,应该是绑定了企业租户信息 if (wxUser.IsEmpty()|| wxUser.CompanyID.IsNullOrZero()) throw Oops.Oh(ErrorCode.xg1002); wxUser.Type= (int)UserEnum.ADVERTISE; var bsUser = await this.Baseuser.UpdateAsync(wxUser); //获取公司信息 company = await this.CompanyRep.SingleAsync(x=>x.Id == wxUser.CompanyID); authUserOut.companyDto = company.Adapt(); } if (wxUser.IsEmpty() || sysUser.IsEmpty()) throw Oops.Oh(ErrorCode.xg1002); //判断是否存在邀请 if(phoneModel.Scene != null) { //判断当前用户是否被邀请过 var invi = await this.invitaitionRey.FirstOrDefaultAsync(x => x.UserID == UserManager.UserId); if (invi.IsNullOrZero()) { var invres = await this.invitaitionRey.FirstOrDefaultAsync(x => x.InviteID == phoneModel.Scene); //未被邀请 var invrey = await this.invitaitionRey.InsertAsync(new InviteUserPos() { UserID = UserManager.UserId, InviteUserID = (long)phoneModel.Scene, InviteID = invres.InviteID }); if (invrey > 0) { //附加奖励给邀请人 UtilService utilService = new UtilService(this.rechargeRep); utilService.Reward((long)phoneModel.Scene, 1); } } } var Self = await this.Self.FirstOrDefaultAsync(x => x.CreatedUserId == sysUser.Id); // 获取加密后的密码 var encryptPassword = MD5Encryption.Encrypt(sysUser.Password); // 验证账号是否被冻结 if (sysUser.Status == CommonStatus.DISABLE) throw Oops.Oh(ErrorCode.D1017); //获取对应租户 var tenant = this._sysTenantRep.Single(sysUser.TenantId); if (tenant.IsNullOrZero()) throw Oops.Oh(ErrorCode.F1001); // 生成Token令牌 authUserOut.Token = JWTEncryption.Encrypt(new Dictionary { {ClaimConst.CLAINM_USERID, sysUser.Id}, {ClaimConst.TENANT_ID, sysUser.TenantId}, {ClaimConst.CLAINM_ACCOUNT, sysUser.Account}, {ClaimConst.CLAINM_NAME, sysUser.Name}, {ClaimConst.CLAINM_SUPERADMIN, sysUser.AdminType}, { ClaimConst.CLAINM_TENANT_TYPE, tenant.TenantType }, { ClaimConst.CLAINM_TENANT_NAME, tenant.Name }, }); // 设置Swagger自动登录 _httpContextAccessor.HttpContext.SigninToSwagger(authUserOut.Token); // 生成刷新Token令牌 var refreshToken = JWTEncryption.GenerateRefreshToken(authUserOut.Token, 30); // 设置刷新Token令牌 _httpContextAccessor.HttpContext.Response.Headers["x-access-token"] = refreshToken; var httpContext = App.HttpContext; await _eventPublisher.PublishAsync(new ChannelEventSource("Update:UserLoginInfo", new SysUser { Id = sysUser.Id, LastLoginIp = httpContext.GetLocalIpAddressToIPv4(), LastLoginTime = DateTime.Now })); authUserOut.Avatar = sysUser.Avatar; authUserOut.Phone = sysUser.Phone; authUserOut.Sex = sysUser.Sex; authUserOut.UserId = sysUser.Id; authUserOut.UserName = sysUser.Name; authUserOut.Self = Self.IsEmpty() ? null : Self.Name; authUserOut.Type = (UserEnum)wxUser.Type; authUserOut.OpenID = tokenModel.OpenId; authUserOut.Tenant = tenant.Adapt(); authUserOut.Describe = wxUser.Describe; await this.cacheService.SetUserInfoAsync(authUserOut, authUserOut.UserId); return authUserOut; } /// /// 手机端登录(验证码登录) /// /// [HttpPost("/Mini/SignInCode")] [AllowAnonymous] public async Task SignInCodeAsync(PhoneModel phoneModel) { Company company = new Company(); AuthUserOut authUserOut = new AuthUserOut(); //验证电话和验证码一致 var verIfy = await this.cacheService.GetVerifyCode(phoneModel.Phone); if (verIfy != phoneModel.CheckingCode) throw new Exception("验证码错误"); //查询系统用户 var sysUser = this._sysUserRep.AsQueryable() .Filter("TenantId", true) .First(x => x.Phone == phoneModel.Phone.ToString()); //账号不存在 生成系统账号 if (sysUser.IsEmpty()) { sysUser = await this._sysUserRep.InsertReturnEntityAsync(new SysUser() { Account = phoneModel.Phone.ToString(), AdminType = AdminType.None, Avatar = "https://gdzongzhi.com/assets/img/logo.png", Birthday = DateTime.Now, CreatedTime = DateTime.Now, CreatedUserId = null, CreatedUserName = null, Sex = Gender.UNKNOWN, Status = CommonStatus.ENABLE, Email = null, IsDeleted = false, Name = phoneModel.Phone.ToString(), Password = MD5Encryption.Encrypt("123456"), TenantId = 392820661919813, Phone = phoneModel.Phone.ToString(), NickName = "", Tel = null, }); } //读取凭证 var tokenModel = await this._wechatOAuth.GetCode2SessionAsync(phoneModel.Code); var wxUser = await this.Baseuser.AsQueryable() .Filter("TenantId", true) .Where(x => x.OpenID == tokenModel.OpenId).SingleAsync(); //区分不同类型账号 if (phoneModel.LogInType == (int)UserEnum.JOB) { if (wxUser.IsEmpty()) { wxUser = await this.Baseuser.InsertReturnEntityAsync(new BaseUser() { UnionId = tokenModel.Unionid, CreatedUserId = sysUser.Id, CreatedTime = DateTime.Now, CreatedUserName = sysUser.Name, AvatarUrl = "https://gdzongzhi.com/assets/img/logo.png", Status = (int)CommonStatus.ENABLE, OpenID = tokenModel.OpenId, TenantId = 392820661919813, Type = (int)UserEnum.JOB, UserName = phoneModel.Phone.ToString(), }); } else { wxUser.Type = (int)UserEnum.JOB; var bsUser = await this.Baseuser.UpdateAsync(wxUser); } } else { //如果是企业账号,应该是绑定了企业租户信息 if (wxUser.IsEmpty() || wxUser.CompanyID.IsNullOrZero()) throw Oops.Oh(ErrorCode.xg1002); wxUser.Type = (int)UserEnum.ADVERTISE; var bsUser = await this.Baseuser.UpdateAsync(wxUser); //获取公司信息 company = await this.CompanyRep.SingleAsync(x => x.Id == wxUser.CompanyID); authUserOut.companyDto = company.Adapt(); } if (wxUser.IsEmpty() || sysUser.IsEmpty()) throw Oops.Oh(ErrorCode.xg1002); var Self = await this.Self.FirstOrDefaultAsync(x => x.CreatedUserId == sysUser.Id); // 获取加密后的密码 var encryptPassword = MD5Encryption.Encrypt(sysUser.Password); // 验证账号是否被冻结 if (sysUser.Status == CommonStatus.DISABLE) throw Oops.Oh(ErrorCode.D1017); //获取对应租户 var tenant = this._sysTenantRep.Single(sysUser.TenantId); if (tenant.IsNullOrZero()) throw Oops.Oh(ErrorCode.F1001); // 生成Token令牌 authUserOut.Token = JWTEncryption.Encrypt(new Dictionary { {ClaimConst.CLAINM_USERID, sysUser.Id}, {ClaimConst.TENANT_ID, sysUser.TenantId}, {ClaimConst.CLAINM_ACCOUNT, sysUser.Account}, {ClaimConst.CLAINM_NAME, sysUser.Name}, {ClaimConst.CLAINM_SUPERADMIN, sysUser.AdminType}, { ClaimConst.CLAINM_TENANT_TYPE, tenant.TenantType }, { ClaimConst.CLAINM_TENANT_NAME, tenant.Name }, }); // 设置Swagger自动登录 _httpContextAccessor.HttpContext.SigninToSwagger(authUserOut.Token); // 生成刷新Token令牌 var refreshToken = JWTEncryption.GenerateRefreshToken(authUserOut.Token, 30); // 设置刷新Token令牌 _httpContextAccessor.HttpContext.Response.Headers["x-access-token"] = refreshToken; var httpContext = App.HttpContext; await _eventPublisher.PublishAsync(new ChannelEventSource("Update:UserLoginInfo", new SysUser { Id = sysUser.Id, LastLoginIp = httpContext.GetLocalIpAddressToIPv4(), LastLoginTime = DateTime.Now })); authUserOut.Avatar = sysUser.Avatar; authUserOut.Phone = sysUser.Phone; authUserOut.Sex = sysUser.Sex; authUserOut.UserId = sysUser.Id; authUserOut.UserName = sysUser.Name; authUserOut.Self = Self.IsEmpty() ? null : Self.Name; authUserOut.Type = (UserEnum)wxUser.Type; authUserOut.OpenID = tokenModel.OpenId; authUserOut.Tenant = tenant.Adapt(); authUserOut.Describe = wxUser.Describe; await this.cacheService.SetUserInfoAsync(authUserOut, authUserOut.UserId); return authUserOut; } /// /// 发送验证码 /// /// [HttpGet("/Mini/SendTextMessage")] [AllowAnonymous] public async Task SendTextMessage(string phone) { string code = ""; //生成随机数字 Random rand = new Random(); for (int i = 0; i < 6; i++) { code += rand.Next(0, 9).ToString(); } Credential credential = new Credential() { SecretId = await GetConfigCache("TENCENT_SMS_SECRET_ID"), SecretKey =await GetConfigCache("TENCENT_SMS_SECRET_KEY") }; SendSmsRequest sendSmsRequest = new SendSmsRequest() { SignName = await GetConfigCache("TENCENT_SMS_SIGN"), SmsSdkAppId = await GetConfigCache("TENCENT_SMS_SDK_APP_ID"), PhoneNumberSet = new string[] { "+86" + phone }, TemplateId = await GetConfigCache("TENCENT_SMS_TEMPLATEID"), TemplateParamSet = new string[] { code } }; TencentCloudExamples.SendTextMessage(sendSmsRequest,credential); await this.cacheService.SetVerifyCode(phone, code); } /// /// 获取当前登录用户信息 /// /// [HttpGet("/Mini/GetLoginUser")] public async Task GetLoginUserAsync() { return await this.cacheService.GetUserInfoAsync(UserManager.UserId); } /// /// 获取AccessToken /// /// [HttpGet("/Mini/GetAccessToken")] public async Task GetAccessTokenAsync() { var value = await cacheService.GetAccessTokenAsync(); if(value == null) { var token = this._wechatOAuth.GetTokenAsync(); await cacheService.SetAccessTokenAsync(token.Result.AccessToken); value = token.Result.AccessToken; } return value; } /// /// 获取二维分享码 /// /// [HttpGet("/Mini/GetShareCode")] public async Task GetShareCode(string scene, string page) { var url = await this.cacheService.GetQRCodeAsync(UserManager.UserId); if(url != null) return url; var value = await cacheService.GetAccessTokenAsync(); if (value == null) { var token = this._wechatOAuth.GetTokenAsync(); await cacheService.SetAccessTokenAsync(token.Result.AccessToken); value = token.Result.AccessToken; } var res = await this._wechatOAuth.GetShareCodeAsync(value,scene, page); var fileName = Path.Combine("/" + UserManager.UserId + ".png"); try { var filePath = Path.Combine(App.WebHostEnvironment.WebRootPath, _options.QRCode.path); if (!Directory.Exists(filePath)) Directory.CreateDirectory(filePath); MemoryStream ms = new MemoryStream(res); FileStream fs = new FileStream(filePath + fileName, FileMode.OpenOrCreate); ms.WriteTo(fs); ms.Close(); fs.Close(); await this.cacheService.SetQRCodeAsync(UserManager.UserId,fileName); } catch (Exception e) { throw Oops.Oh(e.Message); } return fileName; } /// /// 获取配置信息 /// /// /// private async Task GetConfigCache(string code) { var value = await _sysCacheService.GetAsync(code); if (string.IsNullOrEmpty(value)) { var config = await _sysConfigRep.FirstOrDefaultAsync(u => u.Code == code); value = config != null ? config.Value : ""; await _sysCacheService.SetAsync(code, value); } return value; } } }