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.

575 lines
23 KiB

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;
using System.Diagnostics;
namespace GDZZ.Application.Service.Auth
{
[ApiDescriptionSettings("Application", Name = "Auth", Order = 1)]
public class AuthService : IAuthService, IDynamicApiController, ITransient
{
#region 仓储
private readonly SqlSugarRepository<BaseUser> Baseuser; // wx用户仓储
private readonly SqlSugarRepository<SysUser> _sysUserRep; // 用户表仓储
private readonly SqlSugarRepository<UserScope> UserScope; //用户业务关联仓储 _
private readonly SqlSugarRepository<SysTenant> _sysTenantRep; //租户仓储
private readonly SqlSugarRepository<SeIF> Self; //职业仓储
private readonly SqlSugarRepository<Company> CompanyRep;
private readonly SqlSugarRepository<MiniPayTake> payTakeRep; //支付仓储
private readonly SqlSugarRepository<SysConfig> _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<InviteUserPos> invitaitionRey;
private readonly SqlSugarRepository<MiniRecharge> rechargeRep; //充值仓储
#endregion
/// <summary>
/// 获取配置文件
/// </summary>
private readonly ThirdParty _oauthConfig;
public AuthService(
IOptions<OAuthOptions> options,
SqlSugarRepository<UserScope> UserScope,
SqlSugarRepository<BaseUser> Baseuser,
SqlSugarRepository<SysTenant> sysTenantRep,
SqlSugarRepository<SysUser> sysUserRep,
SqlSugarRepository<SeIF> Self,
SqlSugarRepository<Company> CompanyRep,
SqlSugarRepository<MiniPayTake> payTakeRep,
SqlSugarRepository<SysConfig> sysConfigRep,
SqlSugarRepository<InviteUserPos> invitaitionRey,
SqlSugarRepository<MiniRecharge> rechargeRep,
IOptions<UploadFileOptions> UFoptions,
ISysCacheService sysCacheService,
ICacheService cacheService,
WechatOAuth wechatOAuth,
IEventPublisher eventPublisher,
IHttpContextAccessor httpContextAccessor)
{
this.UserScope = UserScope;
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;
}
/// <summary>
/// 手机端登录(一键登录)
/// </summary>
/// <returns></returns>
[HttpPost("/Mini/SignIn")]
[AllowAnonymous]
public async Task<AuthUserOut> SignInAsync(PhoneModel phoneModel)
{
AuthUserOut authUserOut = new AuthUserOut();
Company company = new Company();
try
{
this.UserScope.CurrentBeginTran(); //开启事务
//读取凭证
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())
{
//wxUser = await this.UserScope.InsertAsync(new UserScope)
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 (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,
UserName = phoneModel.Phone.ToString(),
});
}
//区分账号类型
switch (phoneModel.LogInType)
{
case (int)UserEnum.JOB:
wxUser.Type = (int)UserEnum.JOB;
await this.Baseuser.UpdateAsync(wxUser);
break;
case (int)UserEnum.HEADHUNTERS:
wxUser.Type = (int)UserEnum.HEADHUNTERS;
await this.Baseuser.UpdateAsync(wxUser);
break;
case (int)UserEnum.ADVERTISE:
wxUser.Type = (int)UserEnum.ADVERTISE;
await this.Baseuser.UpdateAsync(wxUser);
//获取公司信息
company = await this.CompanyRep.FirstOrDefaultAsync(x => x.Id == wxUser.CompanyID);
authUserOut.companyDto = company.Adapt<CompanyDto>();
break;
default:
break;
}
if (wxUser.IsEmpty() || sysUser.IsEmpty())
throw Oops.Oh(ErrorCode.xg1002);
var userc = await this.UserScope.FirstOrDefaultAsync(x => x.BaseUserID == wxUser.Id && x.SysUserID == sysUser.Id);
if (userc.IsNullOrZero())
{
await this.UserScope.InsertAsync(new UserScope()
{
SysUserID = sysUser.Id,
BaseUserID = wxUser.Id,
});
}
//判断是否存在邀请
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<string, object>
{
{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<TenantOutput>();
authUserOut.Describe = wxUser.Describe;
await this.cacheService.SetUserInfoAsync(authUserOut, authUserOut.UserId);
this.UserScope.CurrentCommitTran();
}
catch (Exception)
{
this.UserScope.CurrentRollbackTran();
throw;
}
return authUserOut;
}
/// <summary>
/// 手机端登录(验证码登录)
/// </summary>
/// <returns></returns>
[HttpPost("/Mini/SignInCode")]
[AllowAnonymous]
public async Task<AuthUserOut> 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 (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,
UserName = phoneModel.Phone.ToString(),
});
}
//区分账号类型
switch (phoneModel.LogInType)
{
case (int)UserEnum.JOB :
wxUser.Type = (int)UserEnum.JOB;
await this.Baseuser.UpdateAsync(wxUser);
break;
case (int)UserEnum.HEADHUNTERS:
wxUser.Type = (int)UserEnum.HEADHUNTERS;
await this.Baseuser.UpdateAsync(wxUser);
break;
case (int)UserEnum.ADVERTISE:
wxUser.Type = (int)UserEnum.ADVERTISE;
await this.Baseuser.UpdateAsync(wxUser);
//获取公司信息
company = await this.CompanyRep.FirstOrDefaultAsync(x => x.Id == wxUser.CompanyID);
authUserOut.companyDto = company.Adapt<CompanyDto>();
break;
default:
break;
}
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<string, object>
{
{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<TenantOutput>();
authUserOut.Describe = wxUser.Describe;
await this.cacheService.SetUserInfoAsync(authUserOut, authUserOut.UserId);
return authUserOut;
}
/// <summary>
/// 发送验证码
/// </summary>
/// <returns></returns>
[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);
}
/// <summary>
/// 获取当前登录用户信息
/// </summary>
/// <returns></returns>
[HttpGet("/Mini/GetLoginUser")]
public async Task<AuthUserOut> GetLoginUserAsync()
{
return await this.cacheService.GetUserInfoAsync(UserManager.UserId);
}
/// <summary>
/// 获取AccessToken
/// </summary>
/// <returns></returns>
[HttpGet("/Mini/GetAccessToken")]
public async Task<string> 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;
}
/// <summary>
/// 获取二维分享码
/// </summary>
/// <returns></returns>
[HttpGet("/Mini/GetShareCode")]
public async Task<string> 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;
}
/// <summary>
/// 获取配置信息
/// </summary>
/// <param name="code"></param>
/// <returns></returns>
private async Task<dynamic> 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;
}
}
}