using System; using System.Net.Http; using IRaCIS.Application.Interfaces; using IRaCIS.Application.Contracts; using IRaCIS.Core.Application.Auth; using IRaCIS.Core.Application.Filter; using IRaCIS.Core.Domain.Share; using IRaCIS.Core.Infrastructure.Extention; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using System.Collections.Generic; using System.Text; using Microsoft.AspNetCore.Http; using IRaCIS.Core.Application.Interfaces; using System.Threading.Tasks; using IRaCIS.Core.Application.Service; using IRaCIS.Core.Infra.EFCore; using IRaCIS.Core.Domain.Models; using IRaCIS.Core.Infrastructure; using System.Linq; using Microsoft.Extensions.Logging; using MassTransit; using Microsoft.AspNetCore.Hosting; using IRaCIS.Core.Application.Helper; using Microsoft.Extensions.Options; using IRaCIS.Core.Application.Contracts; using LoginReturnDTO = IRaCIS.Application.Contracts.LoginReturnDTO; using DocumentFormat.OpenXml.Spreadsheet; using AutoMapper.QueryableExtensions; using NetTopologySuite.Algorithm; using ZiggyCreatures.Caching.Fusion; using AlibabaCloud.SDK.Sts20150401; using AlibabaCloud.SDK.Sts20150401.Models; using Org.BouncyCastle.Tls; using Amazon.SecurityToken.Model; using Amazon.SecurityToken; using Amazon; using AssumeRoleRequest = Amazon.SecurityToken.Model.AssumeRoleRequest; using AutoMapper; namespace IRaCIS.Api.Controllers { /// /// 医生基本信息 、工作信息 专业信息、审核状态 /// [ApiController, ApiExplorerSettings(GroupName = "Reviewer")] public class ExtraController : ControllerBase { /// /// 获取医生详情 /// /// /// /// /// /// /// /// /// [HttpGet, Route("doctor/getDetail/{doctorId:guid}")] public async Task> GetDoctorDetail([FromServices] IAttachmentService attachmentService, [FromServices] IDoctorService _doctorService, [FromServices] IEducationService _educationService, [FromServices] ITrialExperienceService _trialExperienceService, [FromServices] IResearchPublicationService _researchPublicationService, [FromServices] IVacationService _vacationService, Guid doctorId) { var education = await _educationService.GetEducation(doctorId); var sowList = _doctorService.GetDoctorSowList(doctorId); var ackSowList = _doctorService.GetDoctorAckSowList(doctorId); var doctorDetail = new DoctorDetailDTO { AuditView = await _doctorService.GetAuditState(doctorId), BasicInfoView = await _doctorService.GetBasicInfo(doctorId), EmploymentView = await _doctorService.GetEmploymentInfo(doctorId), AttachmentList = await attachmentService.GetAttachments(doctorId), EducationList = education.EducationList, PostgraduateList = education.PostgraduateList, TrialExperienceView = await _trialExperienceService.GetTrialExperience(doctorId), ResearchPublicationView = await _researchPublicationService.GetResearchPublication(doctorId), SpecialtyView = await _doctorService.GetSpecialtyInfo(doctorId), InHoliday = (await _vacationService.OnVacation(doctorId)).IsSuccess, IntoGroupInfo = _doctorService.GetDoctorIntoGroupInfo(doctorId), SowList = sowList, AckSowList = ackSowList }; return ResponseOutput.Ok(doctorDetail); } /// 系统用户登录接口[New] [HttpPost, Route("user/login")] [AllowAnonymous] public async Task Login(UserLoginDTO loginUser, [FromServices] IFusionCache _fusionCache, [FromServices] IUserService _userService, [FromServices] ITokenService _tokenService, [FromServices] IReadingImageTaskService readingImageTaskService, [FromServices] IOptionsMonitor _verifyConfig, [FromServices] IOptionsMonitor _emailConfig, [FromServices] IMailVerificationService _mailVerificationService) { var emailConfig = _emailConfig.CurrentValue; var companyInfo = new SystemEmailSendConfigView() { CompanyName = emailConfig.CompanyName, CompanyNameCN = emailConfig.CompanyNameCN, CompanyShortName = emailConfig.CompanyShortName, CompanyShortNameCN = emailConfig.CompanyShortNameCN }; //MFA 邮箱验证 前端传递用户Id 和MFACode if (loginUser.UserId != null && _verifyConfig.CurrentValue.OpenLoginMFA) { Guid userId = (Guid)loginUser.UserId; //验证MFA 编码是否有问题 ,前端要拆开,自己调用验证的逻辑 //await _userService.VerifyMFACodeAsync(userId, loginUser.MFACode); //var loginUser = await _userRepository.Where(u => u.UserName.Equals(userName) && u.Password == password).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync(); var basicInfo = await _userService.GetUserBasicInfo(userId, loginUser.Password); var loginReturn = new LoginReturnDTO() { BasicInfo = basicInfo }; loginReturn.JWTStr = _tokenService.GetToken(IRaCISClaims.Create(loginReturn.BasicInfo)); // 创建一个 CookieOptions 对象,用于设置 Cookie 的属性 var option = new CookieOptions { Expires = DateTime.Now.AddMonths(1), // 设置过期时间为 30 分钟之后 HttpOnly = false, // 确保 cookie 只能通过 HTTP 访问 SameSite = Microsoft.AspNetCore.Http.SameSiteMode.None, // 设置 SameSite 属性 Secure = false // 确保 cookie 只能通过 HTTPS 访问 }; HttpContext.Response.Cookies.Append("access_token", loginReturn.JWTStr, option); // 验证阅片休息时间 await readingImageTaskService.ResetReadingRestTime(userId); await _fusionCache.SetAsync(CacheKeys.UserToken(userId), loginReturn.JWTStr, TimeSpan.FromDays(7)); await _fusionCache.SetAsync(CacheKeys.UserAutoLoginOut(userId), DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TimeSpan.FromMinutes(_verifyConfig.CurrentValue.AutoLoginOutMinutes)); loginReturn.CompanyInfo = companyInfo; return ResponseOutput.Ok(loginReturn); } else { var returnModel = await _userService.Login(loginUser.UserName, loginUser.Password); if (returnModel.IsSuccess) { #region GRPC 调用鉴权中心,因为服务器IIS问题 http/2 故而没法使用 ////重试策略 //var defaultMethodConfig = new MethodConfig //{ // Names = { MethodName.Default }, // RetryPolicy = new RetryPolicy // { // MaxAttempts = 3, // InitialBackoff = TimeSpan.FromSeconds(1), // MaxBackoff = TimeSpan.FromSeconds(5), // BackoffMultiplier = 1.5, // RetryableStatusCodes = { Grpc.Core.StatusCode.Unavailable } // } //}; //#region unable to trust the certificate then the gRPC client can be configured to ignore the invalid certificate //var httpHandler = new HttpClientHandler(); //// Return `true` to allow certificates that are untrusted/invalid //httpHandler.ServerCertificateCustomValidationCallback = // HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; //////这一句是让grpc支持本地 http 如果本地访问部署在服务器上,那么是访问不成功的 //AppContext.SetSwitch( // "System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); //#endregion //var grpcAdress = configuration.GetValue("GrpcAddress"); ////var grpcAdress = "http://localhost:7200"; //var channel = GrpcChannel.ForAddress(grpcAdress, new GrpcChannelOptions //{ // HttpHandler = httpHandler, // ServiceConfig = new ServiceConfig { MethodConfigs = { defaultMethodConfig } } //}); ////var channel = GrpcChannel.ForAddress(grpcAdress); //var grpcClient = new TokenGrpcService.TokenGrpcServiceClient(channel); //var userInfo = returnModel.Data.BasicInfo; //var tokenResponse = grpcClient.GetUserToken(new GetTokenReuqest() //{ // Id = userInfo.Id.ToString(), // ReviewerCode = userInfo.ReviewerCode, // IsAdmin = userInfo.IsAdmin, // RealName = userInfo.RealName, // UserTypeEnumInt = (int)userInfo.UserTypeEnum, // UserTypeShortName = userInfo.UserTypeShortName, // UserName = userInfo.UserName //}); //returnModel.Data.JWTStr = tokenResponse.Token; #endregion var userId = returnModel.Data.BasicInfo.Id; if (_verifyConfig.CurrentValue.OpenLoginMFA) { //MFA 发送邮件 returnModel.Data.IsMFA = true; var email = returnModel.Data.BasicInfo.EMail; var hiddenEmail = IRCEmailPasswordHelper.MaskEmail(email); returnModel.Data.BasicInfo.EMail = hiddenEmail; //修改密码 if (returnModel.Data.BasicInfo.IsFirstAdd || returnModel.Data.BasicInfo.LoginState == 1) { returnModel.Data.JWTStr = _tokenService.GetToken(IRaCISClaims.Create(returnModel.Data.BasicInfo)); } else { //正常登录才发送邮件 await _userService.SendMFAEmail(userId); } } else { returnModel.Data.JWTStr = _tokenService.GetToken(IRaCISClaims.Create(returnModel.Data.BasicInfo)); // 创建一个 CookieOptions 对象,用于设置 Cookie 的属性 var option = new CookieOptions { Expires = DateTime.Now.AddMonths(1), // 设置过期时间为 30 分钟之后 HttpOnly = false, // 确保 cookie 只能通过 HTTP 访问 SameSite = Microsoft.AspNetCore.Http.SameSiteMode.None, // 设置 SameSite 属性 Secure = false // 确保 cookie 只能通过 HTTPS 访问 }; HttpContext.Response.Cookies.Append("access_token", returnModel.Data.JWTStr, option); // 验证阅片休息时间 await readingImageTaskService.ResetReadingRestTime(returnModel.Data.BasicInfo.Id); await _fusionCache.SetAsync(CacheKeys.UserToken(userId), returnModel.Data.JWTStr, TimeSpan.FromDays(7)); await _fusionCache.SetAsync(CacheKeys.UserAutoLoginOut(userId), DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TimeSpan.FromMinutes(_verifyConfig.CurrentValue.AutoLoginOutMinutes)); } } returnModel.Data.CompanyInfo = companyInfo; return returnModel; } } [AllowAnonymous] [HttpGet, Route("user/getPublicKey")] public IResponseOutput GetPublicKey([FromServices] IOptionsMonitor _IRCEncreptOption) { //var pemPublicKey = Encoding.UTF8.GetString(Convert.FromBase64String(_IRCEncreptOption.CurrentValue.Base64RSAPublicKey)); return ResponseOutput.Ok(_IRCEncreptOption.CurrentValue.Base64RSAPublicKey); } [HttpGet, Route("imageShare/ShareImage")] [AllowAnonymous] public IResponseOutput ShareImage([FromServices] ITokenService _tokenService) { var token = _tokenService.GetToken(IRaCISClaims.Create(new UserBasicInfo() { Id = Guid.Empty, IsReviewer = false, IsAdmin = false, RealName = "Share001", UserName = "Share001", Sex = 0, //UserType = "ShareType", UserTypeEnum = UserTypeEnum.ShareImage, Code = "ShareCode001", })); return ResponseOutput.Ok("/showdicom?studyId=f7b67793-8155-0223-2f15-118f2642efb8&type=Share&token=" + token); } [HttpGet("user/GetObjectStoreToken")] public async Task GetObjectStoreTokenAsync([FromServices] IOptionsMonitor options, [FromServices] IOSSService _oSSService) { var result = await _oSSService.GetObjectStoreTempToken(); result.AWS = await GetAWSTemToken(options.CurrentValue); return ResponseOutput.Ok(result); } private async Task GetAWSTemToken(ObjectStoreServiceOptions serviceOption) { var awsOptions = serviceOption.AWS; //aws 临时凭证 // 创建 STS 客户端 var stsClient = new AmazonSecurityTokenServiceClient(awsOptions.AccessKeyId, awsOptions.SecretAccessKey); // 使用 AssumeRole 请求临时凭证 var assumeRoleRequest = new AssumeRoleRequest { RoleArn = awsOptions.RoleArn, // 角色 ARN RoleSessionName = $"session-name-{NewId.NextGuid()}", DurationSeconds = awsOptions.DurationSeconds // 临时凭证有效期 }; var assumeRoleResponse = await stsClient.AssumeRoleAsync(assumeRoleRequest); var credentials = assumeRoleResponse.Credentials; var tempToken = new AWSTempToken() { AccessKeyId = credentials.AccessKeyId, SecretAccessKey = credentials.SecretAccessKey, SessionToken = credentials.SessionToken, Expiration = credentials.Expiration, Region = awsOptions.Region, BucketName = awsOptions.BucketName, EndPoint = awsOptions.EndPoint, ViewEndpoint = awsOptions.ViewEndpoint, }; return tempToken; } #region aliyun-net-sdk-sts 之前 //[HttpGet("user/GenerateSTS")] //public IResponseOutput GenerateSTS([FromServices] IOptionsMonitor options) //{ // var ossOptions = options.CurrentValue; // IClientProfile profile = DefaultProfile.GetProfile(ossOptions.regionId, ossOptions.accessKeyId, ossOptions.accessKeySecret); // DefaultAcsClient client = new DefaultAcsClient(profile); // // 创建一个STS请求 // AssumeRoleRequest request = new AssumeRoleRequest // { // RoleArn = ossOptions.roleArn, // 角色ARN,需要替换为你的角色ARN // RoleSessionName = $"session-name-{NewId.NextGuid()}", // 角色会话名称,可自定义 // DurationSeconds = 900, // 令牌有效期(单位:秒),这里设置为1小时 // }; // AssumeRoleResponse response = client.GetAcsResponse(request); // // 返回STS令牌信息给前端 // var stsToken = new // { // AccessKeyId = response.Credentials.AccessKeyId, // AccessKeySecret = response.Credentials.AccessKeySecret, // SecurityToken = response.Credentials.SecurityToken, // Expiration = response.Credentials.Expiration, // Region = ossOptions.region, // BucketName = ossOptions.bucketName, // ViewEndpoint = ossOptions.viewEndpoint, // }; // return ResponseOutput.Ok(stsToken); //} #endregion [HttpGet("User/UserRedirect")] [AllowAnonymous] public async Task UserRedirect([FromServices] IRepository _userRepository, string url, [FromServices] ILogger _logger) { var decodeUrl = System.Web.HttpUtility.UrlDecode(url); var userId = decodeUrl.Substring(decodeUrl.IndexOf("UserId=") + "UserId=".Length, 36); var token = decodeUrl.Substring(decodeUrl.IndexOf("access_token=") + "access_token=".Length); var lang = decodeUrl.Substring(decodeUrl.IndexOf("lang=") + "lang=".Length, 2); var domainStrList = decodeUrl.Split("/").ToList().Take(3).ToList(); var errorUrl = domainStrList[0] + "//" + domainStrList[2] + "/error"; if (!await _userRepository.AnyAsync(t => t.Id == Guid.Parse(userId) && t.EmailToken == token && t.IsFirstAdd)) { decodeUrl = errorUrl + $"?lang={lang}&ErrorMessage={System.Web.HttpUtility.UrlEncode(lang == "zh" ? "您的初始化链接已过期" : "Error!The initialization link has expired. Return")} "; } return Redirect(decodeUrl); } [HttpGet, Route("ip")] [AllowAnonymous] public IResponseOutput Get([FromServices] IHttpContextAccessor _context) { StringBuilder sb = new StringBuilder(); sb.AppendLine($"RemoteIpAddress:{_context.HttpContext.Connection.RemoteIpAddress}"); if (Request.Headers.ContainsKey("X-Real-IP")) { sb.AppendLine($"X-Real-IP:{Request.Headers["X-Real-IP"].ToString()}"); } if (Request.Headers.ContainsKey("X-Forwarded-For")) { sb.AppendLine($"X-Forwarded-For:{Request.Headers["X-Forwarded-For"].ToString()}"); } return ResponseOutput.Ok(sb.ToString()); } [HttpGet, Route("ip2")] [AllowAnonymous] public IResponseOutput Get2([FromServices] IHttpContextAccessor _context) { StringBuilder sb = new StringBuilder(); sb.AppendLine($"RemoteIpAddress:{_context.HttpContext.Connection.RemoteIpAddress}"); if (Request.Headers.ContainsKey("X-Real-IP")) { sb.AppendLine($"X-Real-IP:{Request.Headers["X-Real-IP"].ToString()}"); } if (Request.Headers.ContainsKey("X-Forwarded-For")) { sb.AppendLine($"X-Forwarded-For:{Request.Headers["X-Forwarded-For"].ToString()}"); } return ResponseOutput.Ok(sb.ToString()); } } }