492 lines
20 KiB
C#
492 lines
20 KiB
C#
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
|
||
{
|
||
/// <summary>
|
||
/// 医生基本信息 、工作信息 专业信息、审核状态
|
||
/// </summary>
|
||
[ApiController, ApiExplorerSettings(GroupName = "Reviewer")]
|
||
public class ExtraController : ControllerBase
|
||
{
|
||
|
||
|
||
/// <summary>
|
||
/// 获取医生详情
|
||
/// </summary>
|
||
/// <param name="attachmentService"></param>
|
||
/// <param name="_doctorService"></param>
|
||
/// <param name="_educationService"></param>
|
||
/// <param name="_trialExperienceService"></param>
|
||
/// <param name="_researchPublicationService"></param>
|
||
/// <param name="_vacationService"></param>
|
||
/// <param name="doctorId"></param>
|
||
/// <returns></returns>
|
||
[HttpGet, Route("doctor/getDetail/{doctorId:guid}")]
|
||
|
||
public async Task<IResponseOutput<DoctorDetailDTO>> 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);
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
/// <summary> 系统用户登录接口[New] </summary>
|
||
[HttpPost, Route("user/login")]
|
||
[AllowAnonymous]
|
||
public async Task<IResponseOutput> Login(UserLoginDTO loginUser,
|
||
[FromServices] IFusionCache _fusionCache,
|
||
[FromServices] IUserService _userService,
|
||
[FromServices] ITokenService _tokenService,
|
||
[FromServices] IReadingImageTaskService readingImageTaskService,
|
||
[FromServices] IOptionsMonitor<ServiceVerifyConfigOption> _verifyConfig,
|
||
[FromServices] IOptionsMonitor<SystemEmailSendConfig> _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<UserBasicInfo>(_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<string>("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> _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<IResponseOutput> GetObjectStoreTokenAsync([FromServices] IOptionsMonitor<ObjectStoreServiceOptions> options, [FromServices] IOSSService _oSSService)
|
||
{
|
||
|
||
var result = await _oSSService.GetObjectStoreTempToken();
|
||
|
||
result.AWS = await GetAWSTemToken(options.CurrentValue);
|
||
|
||
return ResponseOutput.Ok(result);
|
||
|
||
}
|
||
|
||
|
||
private async Task<AWSTempToken> 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<AliyunOSSOptions> 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<IActionResult> UserRedirect([FromServices] IRepository<User> _userRepository, string url, [FromServices] ILogger<ExtraController> _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());
|
||
}
|
||
|
||
}
|
||
}
|