添加项目文件。
parent
b620fcb0cf
commit
dc78fd1a09
|
@ -0,0 +1,95 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31903.59
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRaCIS.Core.API", "IRaCIS.Core.API\IRaCIS.Core.API.csproj", "{F15CE209-6039-46A6-AE7F-E81ADA795F28}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRaCIS.Core.Domain", "IRaCIS.Core.Domain\IRaCIS.Core.Domain.csproj", "{D4DF27AC-3739-4264-BFB8-AED6DC2B84C7}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRaCIS.Core.Application", "IRaCIS.Core.Application\IRaCIS.Core.Application.csproj", "{037E7DE9-0AE2-4987-8C69-F51D5B9CA19D}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRaCIS.Core.Domain.Share", "IRaCIS.Core.Domain.Share\IRaCIS.Core.Domain.Share.csproj", "{7CBC76F5-3817-46B7-8D9D-79253A89B578}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRaCIS.JWT.RS256", "ZhiZhunAuthenticationCenter\IRaCIS.JWT.RS256.csproj", "{3EF210EE-D5D1-4C93-A8FA-E0DB1852BD39}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRaCIS.Core.Test", "IRaCIS.Core.Test\IRaCIS.Core.Test.csproj", "{3292B2B4-6E8A-43AA-84C0-AB4A391E8A2A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRaCIS.Core.Infra.EFCore", "IRaCIS.Core.Infra.EFCore\IRaCIS.Core.Infra.EFCore.csproj", "{6D8115E5-84D6-424B-8F8D-0C2D40347A8C}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRaCIS.Core.HttpReports", "IRaCIS.Core.HttpReports\IRaCIS.Core.HttpReports.csproj", "{09A6A5BE-FE36-4822-A405-432876B284A3}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRaCIS.Core.Infrastructure", "IRaCIS.Core.Infrastructure\IRaCIS.Core.Infrastructure.csproj", "{07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "IRaCIS.AuthenticationCenter", "IRaCIS.AuthenticationCenter", "{481329D6-B8A0-491F-A398-1DF66A0FBB62}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRaCIS.Core.IdentityServer4", "IRaCIS.Core.IdentityServer4\IRaCIS.Core.IdentityServer4.csproj", "{C3DD48CF-B8B3-40F6-9BDB-B7C7F0851674}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRaCIS.Core.IdentityServer4.MVC", "IRaCIS.Core.IdentityServer4.MVC\IRaCIS.Core.IdentityServer4.MVC.csproj", "{F621ADD6-94E8-4A4B-998E-25B8EF15D39C}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "LearningTest", "LearningTest", "{520F583F-FB33-4CA5-AF4E-577BF40389CE}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{F15CE209-6039-46A6-AE7F-E81ADA795F28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F15CE209-6039-46A6-AE7F-E81ADA795F28}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F15CE209-6039-46A6-AE7F-E81ADA795F28}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F15CE209-6039-46A6-AE7F-E81ADA795F28}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D4DF27AC-3739-4264-BFB8-AED6DC2B84C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D4DF27AC-3739-4264-BFB8-AED6DC2B84C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D4DF27AC-3739-4264-BFB8-AED6DC2B84C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D4DF27AC-3739-4264-BFB8-AED6DC2B84C7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{037E7DE9-0AE2-4987-8C69-F51D5B9CA19D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{037E7DE9-0AE2-4987-8C69-F51D5B9CA19D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{037E7DE9-0AE2-4987-8C69-F51D5B9CA19D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{037E7DE9-0AE2-4987-8C69-F51D5B9CA19D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7CBC76F5-3817-46B7-8D9D-79253A89B578}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7CBC76F5-3817-46B7-8D9D-79253A89B578}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7CBC76F5-3817-46B7-8D9D-79253A89B578}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7CBC76F5-3817-46B7-8D9D-79253A89B578}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3EF210EE-D5D1-4C93-A8FA-E0DB1852BD39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3EF210EE-D5D1-4C93-A8FA-E0DB1852BD39}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3EF210EE-D5D1-4C93-A8FA-E0DB1852BD39}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3EF210EE-D5D1-4C93-A8FA-E0DB1852BD39}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3292B2B4-6E8A-43AA-84C0-AB4A391E8A2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3292B2B4-6E8A-43AA-84C0-AB4A391E8A2A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3292B2B4-6E8A-43AA-84C0-AB4A391E8A2A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3292B2B4-6E8A-43AA-84C0-AB4A391E8A2A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6D8115E5-84D6-424B-8F8D-0C2D40347A8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6D8115E5-84D6-424B-8F8D-0C2D40347A8C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6D8115E5-84D6-424B-8F8D-0C2D40347A8C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6D8115E5-84D6-424B-8F8D-0C2D40347A8C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{09A6A5BE-FE36-4822-A405-432876B284A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{09A6A5BE-FE36-4822-A405-432876B284A3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{09A6A5BE-FE36-4822-A405-432876B284A3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{09A6A5BE-FE36-4822-A405-432876B284A3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C3DD48CF-B8B3-40F6-9BDB-B7C7F0851674}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C3DD48CF-B8B3-40F6-9BDB-B7C7F0851674}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C3DD48CF-B8B3-40F6-9BDB-B7C7F0851674}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C3DD48CF-B8B3-40F6-9BDB-B7C7F0851674}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F621ADD6-94E8-4A4B-998E-25B8EF15D39C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F621ADD6-94E8-4A4B-998E-25B8EF15D39C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F621ADD6-94E8-4A4B-998E-25B8EF15D39C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F621ADD6-94E8-4A4B-998E-25B8EF15D39C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{3EF210EE-D5D1-4C93-A8FA-E0DB1852BD39} = {481329D6-B8A0-491F-A398-1DF66A0FBB62}
|
||||
{09A6A5BE-FE36-4822-A405-432876B284A3} = {520F583F-FB33-4CA5-AF4E-577BF40389CE}
|
||||
{C3DD48CF-B8B3-40F6-9BDB-B7C7F0851674} = {481329D6-B8A0-491F-A398-1DF66A0FBB62}
|
||||
{F621ADD6-94E8-4A4B-998E-25B8EF15D39C} = {481329D6-B8A0-491F-A398-1DF66A0FBB62}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {BCC2EB19-3914-489B-B1D7-B7303E0218A3}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"version": 1,
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"dotnet-ef": {
|
||||
"version": "5.0.9",
|
||||
"commands": [
|
||||
"dotnet-ef"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
{
|
||||
"needAnonymizeTag": [
|
||||
{ //PatientsName
|
||||
"Group": "0010",
|
||||
"Element": "0010",
|
||||
"ReplaceValue": "",
|
||||
"Enable": true
|
||||
},
|
||||
{ // PatientID
|
||||
"Group": "0010",
|
||||
"Element": "0020",
|
||||
"ReplaceValue": "",
|
||||
"Enable": true
|
||||
},
|
||||
{ // IssuerOfPatientID
|
||||
"Group": "0010",
|
||||
"Element": "0021",
|
||||
"ReplaceValue": "",
|
||||
"Enable": true
|
||||
},
|
||||
{ // PatientsBirthDate
|
||||
"Group": "0010",
|
||||
"Element": "0030",
|
||||
"ReplaceValue": "",
|
||||
"Enable": true
|
||||
},
|
||||
{ // PatientsBirthTime
|
||||
"Group": "0010",
|
||||
"Element": "0032",
|
||||
"ReplaceValue": "",
|
||||
"Enable": false
|
||||
},
|
||||
{ // PatientsSex
|
||||
"Group": "0010",
|
||||
"Element": "0040",
|
||||
"ReplaceValue": "",
|
||||
"Enable": false
|
||||
},
|
||||
{ // OtherPatientIDs
|
||||
"Group": "0010",
|
||||
"Element": "1000",
|
||||
"ReplaceValue": "",
|
||||
"Enable": false
|
||||
},
|
||||
{ // OtherPatientNames
|
||||
"Group": "0010",
|
||||
"Element": "1001",
|
||||
"ReplaceValue": "",
|
||||
"Enable": false
|
||||
},
|
||||
{ // OtherPatientNames
|
||||
"Group": "0010",
|
||||
"Element": "1005",
|
||||
"ReplaceValue": "",
|
||||
"Enable": true
|
||||
},
|
||||
{ // PatientBirthName
|
||||
"Group": "0010",
|
||||
"Element": "1005",
|
||||
"ReplaceValue": "",
|
||||
"Enable": true
|
||||
},
|
||||
{ // PatientsAge
|
||||
"Group": "0010",
|
||||
"Element": "1010",
|
||||
"ReplaceValue": "",
|
||||
"Enable": true
|
||||
},
|
||||
{ // PatientsAddress
|
||||
"Group": "0010",
|
||||
"Element": "1040",
|
||||
"ReplaceValue": "",
|
||||
"Enable": true
|
||||
},
|
||||
{ // PatientsMothersBirthName
|
||||
"Group": "0010",
|
||||
"Element": "1060",
|
||||
"ReplaceValue": "",
|
||||
"Enable": true
|
||||
},
|
||||
{
|
||||
"Group": "0010",
|
||||
"Element": "2150",
|
||||
"ReplaceValue": "",
|
||||
"Enable": true
|
||||
},
|
||||
{
|
||||
"Group": "0010",
|
||||
"Element": "2152",
|
||||
"ReplaceValue": "",
|
||||
"Enable": true
|
||||
},
|
||||
{
|
||||
"Group": "0010",
|
||||
"Element": "2154",
|
||||
"ReplaceValue": "",
|
||||
"Enable": true
|
||||
},
|
||||
{
|
||||
"Group": "0012",
|
||||
"Element": "0040",
|
||||
"ReplaceValue": "XXX",
|
||||
"Enable": true
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
//using AutoMapper;
|
||||
//using AutoMapper.QueryableExtensions;
|
||||
//using IRaCIS.Application.ViewModels;
|
||||
//using IRaCIS.Core.Application.Contracts;
|
||||
//using IRaCIS.Core.Application.Filter;
|
||||
//using IRaCIS.Core.Application.MediatR.CommandAndQueries;
|
||||
//using IRaCIS.Core.Domain.Models;
|
||||
//using IRaCIS.Core.Domain.Share;
|
||||
//using IRaCIS.Core.Infra.EFCore;
|
||||
//using IRaCIS.Core.Infrastructure.Extention;
|
||||
//using Magicodes.ExporterAndImporter.Core;
|
||||
//using Magicodes.ExporterAndImporter.Excel;
|
||||
//using MediatR;
|
||||
//using Microsoft.AspNetCore.Hosting;
|
||||
//using Microsoft.AspNetCore.Http;
|
||||
//using Microsoft.AspNetCore.Mvc;
|
||||
//using Microsoft.AspNetCore.WebUtilities;
|
||||
//using Microsoft.Net.Http.Headers;
|
||||
//using System;
|
||||
//using System.IO;
|
||||
//using System.Linq;
|
||||
//using System.Threading.Tasks;
|
||||
|
||||
//namespace IRaCIS.Core.API.Controllers
|
||||
//{
|
||||
// [ApiExplorerSettings(GroupName = "Image")]
|
||||
// [ApiController]
|
||||
// public class DownLoadController : ControllerBase
|
||||
// {
|
||||
// public IMapper _mapper { get; set; }
|
||||
// public IUserInfo _userInfo { get; set; }
|
||||
// private readonly IMediator _mediator;
|
||||
|
||||
// private readonly IWebHostEnvironment _hostEnvironment;
|
||||
|
||||
// private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
|
||||
// public DownLoadController(IMapper mapper, IUserInfo userInfo, IMediator mediator, IWebHostEnvironment hostEnvironment, IServiceProvider serviceProvider)
|
||||
// {
|
||||
// _serviceProvider = serviceProvider;
|
||||
// _hostEnvironment = hostEnvironment;
|
||||
// _mediator = mediator;
|
||||
// _mapper = mapper;
|
||||
// _userInfo = userInfo;
|
||||
// }
|
||||
|
||||
// [HttpGet("VisitPlan/DownloadInflunceStudyList{trialId:guid}/{createTime:dateTime}")]
|
||||
// public async Task<IActionResult> DownloadInflunceStudyList(Guid trialId, DateTime createTime, [FromServices] IRepository<VisitPlanInfluenceSubjectVisit> _influnceRepository)
|
||||
// {
|
||||
// var list = _influnceRepository.Where(t => t.TrialId == trialId && t.CreateTime == createTime)
|
||||
// .ProjectTo<VisitPlanInfluenceSubjectVisitDTO>(_mapper.ConfigurationProvider).ToList();
|
||||
|
||||
// if(list.Count == 0)
|
||||
// {
|
||||
// list.Add(new VisitPlanInfluenceSubjectVisitDTO() { CreateTime=DateTime.Now,SubjectCode="test",StudyTime=DateTime.Now,IsDicomStudy=false,HistoryWindow="test"});
|
||||
// }
|
||||
|
||||
// IExporter exporter = new ExcelExporter();
|
||||
|
||||
// var result = await exporter.ExportAsByteArray(list);
|
||||
|
||||
|
||||
// return new XlsxFileResult(bytes: bytes);
|
||||
|
||||
// //return File(result, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", $"检查导出_{DateTime.Now}.xlsx");
|
||||
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
// }
|
||||
//}
|
|
@ -0,0 +1,39 @@
|
|||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Panda.DynamicWebApi.Attributes;
|
||||
|
||||
namespace EasyCaching.Demo.Interceptors.Controllers
|
||||
{
|
||||
|
||||
[NonDynamicWebApi]
|
||||
public class ErrorController : ControllerBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 主要处理 前端404等错误 全局业务异常已统一处理了,非业务错误会来到这里
|
||||
/// </summary>
|
||||
/// <param name="code"></param>
|
||||
/// <returns></returns>
|
||||
[Route("error/{code:int}")]
|
||||
[HttpGet]
|
||||
public IResponseOutput Error(int code)
|
||||
{
|
||||
|
||||
if (code < 500)
|
||||
{
|
||||
//LogDashboard 要求返回码必须是401不能覆盖,否则 认证有问题
|
||||
if (code == 401)
|
||||
{
|
||||
ControllerContext.HttpContext.Response.StatusCode = 401;
|
||||
}
|
||||
|
||||
return ResponseOutput.NotOk($"Client error, actual request error status code({code})");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
return ResponseOutput.NotOk($"Server error , actual request error status code({code})");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,241 @@
|
|||
using System;
|
||||
using System.Net.Http;
|
||||
using EasyCaching.Core;
|
||||
using gRPC.ZHiZHUN.AuthServer.protos;
|
||||
using Grpc.Net.Client;
|
||||
using Grpc.Net.Client.Configuration;
|
||||
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.Application.Services;
|
||||
|
||||
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 = 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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[HttpPost, Route("enroll/downloadResume/{trialId:guid}/{language}")]
|
||||
[TypeFilter(typeof(TrialResourceFilter))]
|
||||
[AllowAnonymous]
|
||||
public async Task<IResponseOutput<string>> DownloadResume([FromServices] IFileService _fileService, int language, Guid trialId, Guid[] doctorIdArray)
|
||||
{
|
||||
var userId = Guid.Parse(User.FindFirst("id").Value);
|
||||
var zipPath = await _fileService.CreateOfficialResumeZip(language, doctorIdArray);
|
||||
|
||||
return ResponseOutput.Ok(zipPath);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary> 系统用户登录接口[New] </summary>
|
||||
[HttpPost, Route("user/login")]
|
||||
[AllowAnonymous]
|
||||
public async Task<IResponseOutput<LoginReturnDTO>> Login(UserLoginDTO loginUser, [FromServices] IEasyCachingProvider provider, [FromServices] IUserService _userService,
|
||||
[FromServices] ITokenService _tokenService, [FromServices] IConfiguration configuration)
|
||||
{
|
||||
|
||||
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
|
||||
|
||||
returnModel.Data.JWTStr = _tokenService.GetToken(IRaCISClaims.Create(returnModel.Data.BasicInfo));
|
||||
|
||||
}
|
||||
|
||||
var userId = returnModel.Data.BasicInfo.Id.ToString();
|
||||
provider.Set(userId, userId, TimeSpan.FromMinutes(AppSettings.LoginExpiredTimeSpan));
|
||||
return returnModel;
|
||||
}
|
||||
|
||||
|
||||
|
||||
[HttpGet, Route("imageShare/ShareImage")]
|
||||
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("/#/preview?studyId=d8e7ed1c-4b59-a483-563d-4f05bd4f8921&token=" + token);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//外部用户 邮件链接调用 以及跳转逻辑
|
||||
|
||||
[HttpGet("trialExternalUser/ExternalUserJoinTrial")]
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> ExternalUserJoinTrial([FromServices] ITrialExternalUserService _trialExternalUserService, Guid trialId, Guid trialExternalUserId, string url)
|
||||
{
|
||||
await _trialExternalUserService.UserConfirmJoinTrial(trialId, trialExternalUserId);
|
||||
|
||||
|
||||
var decodeUrl = System.Web.HttpUtility.UrlDecode(url);
|
||||
|
||||
|
||||
return Redirect(decodeUrl);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[HttpGet, Route("ip")]
|
||||
[AllowAnonymous]
|
||||
public IResponseOutput Get([FromServices] IHttpContextAccessor _context, [FromServices] IUserService _userService)
|
||||
{
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,532 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using IRaCIS.Application.Interfaces;
|
||||
using IRaCIS.Application.Contracts;
|
||||
using IRaCIS.Core.API.Utility;
|
||||
using IRaCIS.Core.Application.Contracts.Image;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using IRaCIS.Core.Application.Filter;
|
||||
using IRaCIS.Core.Application.Contracts;
|
||||
|
||||
namespace IRaCIS.Api.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// 文件上传
|
||||
/// </summary>
|
||||
[Route("file")]
|
||||
[ApiController, Authorize, ApiExplorerSettings(GroupName = "Common")]
|
||||
public class FileController : ControllerBase
|
||||
{
|
||||
private readonly IFileService _fileService;
|
||||
private readonly IWebHostEnvironment _webHostEnvironment;
|
||||
private readonly IHostEnvironment _hostEnvironment;
|
||||
private ILogger<FileController> _logger;
|
||||
|
||||
private string _targetFilePath;
|
||||
private readonly long _fileSizeLimit;
|
||||
private readonly string[] _permittedExtensions = { ".pdf", ".doc", ".docx" };
|
||||
|
||||
public string trustedFileNameForFileStorage = "";
|
||||
|
||||
// Get the default form options so that we can use them to set the default
|
||||
// limits for request body data.
|
||||
private static readonly FormOptions _defaultFormOptions = new FormOptions();
|
||||
private string defaultUploadFilePath = string.Empty;
|
||||
public FileController(IFileService fileService, Microsoft.Extensions.Configuration.IConfiguration config,
|
||||
IHostEnvironment hostEnvironment, ILogger<FileController> logger,
|
||||
IWebHostEnvironment webHostEnvironment)
|
||||
{
|
||||
_fileService = fileService;
|
||||
_hostEnvironment = hostEnvironment;
|
||||
_webHostEnvironment = webHostEnvironment;
|
||||
_fileSizeLimit = config.GetValue<long>("FileSizeLimit");
|
||||
defaultUploadFilePath = Directory.GetParent(_hostEnvironment.ContentRootPath.TrimEnd('\\')).FullName;
|
||||
|
||||
_logger = logger;
|
||||
|
||||
_logger.LogWarning("File Path:" + defaultUploadFilePath);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 上传文件[FileUpload]
|
||||
/// </summary>
|
||||
/// <param name="attachmentType">附件类型</param>
|
||||
/// <param name="doctorId">医生Id</param>
|
||||
/// <returns>返回文件信息</returns>
|
||||
[HttpPost, Route("uploadFile/{attachmentType}/{doctorId}")]
|
||||
[DisableFormValueModelBinding]
|
||||
public async Task<IActionResult> UploadOrdinaryFile(string attachmentType, Guid doctorId)
|
||||
{
|
||||
#region 官方文档方式
|
||||
|
||||
|
||||
if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType))
|
||||
{
|
||||
ModelState.AddModelError("File",
|
||||
$"The request couldn't be processed (Error 1).");
|
||||
// Log error
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
var boundary = MultipartRequestHelper.GetBoundary(
|
||||
MediaTypeHeaderValue.Parse(Request.ContentType),
|
||||
_defaultFormOptions.MultipartBoundaryLengthLimit);
|
||||
var reader = new MultipartReader(boundary, HttpContext.Request.Body);
|
||||
var section = await reader.ReadNextSectionAsync();
|
||||
|
||||
while (section != null)
|
||||
{
|
||||
var hasContentDispositionHeader =
|
||||
ContentDispositionHeaderValue.TryParse(
|
||||
section.ContentDisposition, out var contentDisposition);
|
||||
|
||||
if (hasContentDispositionHeader)
|
||||
{
|
||||
// This check assumes that there's a file
|
||||
// present without form data. If form data
|
||||
// is present, this method immediately fails
|
||||
// and returns the model error.
|
||||
if (!MultipartRequestHelper
|
||||
.HasFileContentDisposition(contentDisposition))
|
||||
{
|
||||
ModelState.AddModelError("File",
|
||||
$"The request couldn't be processed (Error 2).");
|
||||
// Log error
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Don't trust the file name sent by the client. To display
|
||||
// the file name, HTML-encode the value.
|
||||
var trustedFileNameForDisplay = WebUtility.HtmlEncode(
|
||||
contentDisposition.FileName.Value);
|
||||
|
||||
//var trustedFileNameForFileStorage = Path.GetRandomFileName();
|
||||
trustedFileNameForFileStorage = contentDisposition.FileName.Value;
|
||||
|
||||
// **WARNING!**
|
||||
// In the following example, the file is saved without
|
||||
// scanning the file's contents. In most production
|
||||
// scenarios, an anti-virus/anti-malware scanner API
|
||||
// is used on the file before making the file available
|
||||
// for download or for use by other systems.
|
||||
// For more information, see the topic that accompanies
|
||||
// this sample.
|
||||
|
||||
var streamedFileContent = await FileHelpers.ProcessStreamedFile(
|
||||
section, contentDisposition, ModelState,
|
||||
_permittedExtensions, _fileSizeLimit);
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
|
||||
|
||||
//实际文件处理
|
||||
string uploadFolderPath = Path.Combine(defaultUploadFilePath, "UploadFile/" + doctorId + "/");
|
||||
|
||||
var doctorAttachmentUploadFolder = Path.Combine(uploadFolderPath, attachmentType);
|
||||
|
||||
_targetFilePath = doctorAttachmentUploadFolder;
|
||||
|
||||
if (!Directory.Exists(doctorAttachmentUploadFolder)) Directory.CreateDirectory(doctorAttachmentUploadFolder);
|
||||
|
||||
using (var targetStream = System.IO.File.Create(
|
||||
Path.Combine(_targetFilePath, trustedFileNameForFileStorage)))
|
||||
{
|
||||
await targetStream.WriteAsync(streamedFileContent);
|
||||
|
||||
var attachmentPath = $"/UploadFile/{doctorId}/{attachmentType}/{trustedFileNameForFileStorage}";
|
||||
|
||||
return new JsonResult(ResponseOutput.Ok(new UploadFileInfoDTO() { FilePath = attachmentPath, FullFilePath = attachmentPath + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7) }));
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Drain any remaining section body that hasn't been consumed and
|
||||
// read the headers for the next section.
|
||||
section = await reader.ReadNextSectionAsync();
|
||||
}
|
||||
|
||||
|
||||
return Created(nameof(FileController), null);
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 上传文件( 不是医生个人的文件)[FileUpload]
|
||||
/// 例如:阅片章程等
|
||||
/// </summary>
|
||||
/// <param name="type">文件类型</param>
|
||||
/// <returns></returns>
|
||||
|
||||
[HttpPost, Route("uploadNonDoctorFile/{type}")]
|
||||
[DisableFormValueModelBinding]
|
||||
public async Task<IActionResult> UploadNonDoctorFile(string type)
|
||||
{
|
||||
#region New Test 实测OK
|
||||
|
||||
//上传根路径
|
||||
string uploadFolderPath = Path.Combine(defaultUploadFilePath, "UploadFile");
|
||||
|
||||
|
||||
if (uploadFolderPath != null)
|
||||
{
|
||||
//文件类型路径处理
|
||||
var uploadTypePath = Path.Combine(uploadFolderPath, type);
|
||||
if (!Directory.Exists(uploadTypePath)) Directory.CreateDirectory(uploadTypePath);
|
||||
|
||||
|
||||
////实际文件处理
|
||||
|
||||
_targetFilePath = uploadTypePath;
|
||||
|
||||
//获取boundary
|
||||
|
||||
#region 方式二
|
||||
|
||||
var boundary = MultipartRequestHelper.GetBoundary(
|
||||
MediaTypeHeaderValue.Parse(Request.ContentType),
|
||||
_defaultFormOptions.MultipartBoundaryLengthLimit);
|
||||
|
||||
#endregion
|
||||
|
||||
#region 方式一
|
||||
//var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
//得到reader
|
||||
var reader = new MultipartReader(boundary, HttpContext.Request.Body);
|
||||
//{ BodyLengthLimit = 2000 };//
|
||||
var section = await reader.ReadNextSectionAsync();
|
||||
|
||||
//读取section
|
||||
while (section != null)
|
||||
{
|
||||
var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition);
|
||||
if (hasContentDispositionHeader)
|
||||
{
|
||||
trustedFileNameForFileStorage = contentDisposition.FileName.Value;
|
||||
|
||||
|
||||
await WriteFileAsync(section.Body, Path.Combine(_targetFilePath, trustedFileNameForFileStorage));
|
||||
|
||||
#region 方式二
|
||||
|
||||
//using (var targetStream = System.IO.File.Create(
|
||||
// Path.Combine(_targetFilePath, trustedFileNameForFileStorage)))
|
||||
//{
|
||||
|
||||
// using (var memoryStream = new MemoryStream())
|
||||
// {
|
||||
// await section.Body.CopyToAsync(memoryStream);
|
||||
|
||||
// await targetStream.WriteAsync(memoryStream.ToArray());
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
var attachmentPath = $"/UploadFile/{type}/{trustedFileNameForFileStorage}";
|
||||
|
||||
return new JsonResult(ResponseOutput.Ok(new UploadFileInfoDTO() { FilePath = attachmentPath, FullFilePath = attachmentPath + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7) }));
|
||||
}
|
||||
|
||||
section = await reader.ReadNextSectionAsync();
|
||||
}
|
||||
return Created(nameof(FileController), null);
|
||||
|
||||
|
||||
}
|
||||
return new JsonResult(ResponseOutput.NotOk("服务器端映射路径操作失败"));
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 写文件导到磁盘
|
||||
/// </summary>
|
||||
/// <param name="stream">流</param>
|
||||
/// <param name="path">文件保存路径</param>
|
||||
/// <returns></returns>
|
||||
public static async Task<int> WriteFileAsync(System.IO.Stream stream, string path)
|
||||
{
|
||||
const int FILE_WRITE_SIZE = 84975;//写出缓冲区大小
|
||||
int writeCount = 0;
|
||||
using (FileStream fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Write, FILE_WRITE_SIZE, true))
|
||||
{
|
||||
byte[] byteArr = new byte[FILE_WRITE_SIZE];
|
||||
int readCount = 0;
|
||||
while ((readCount = await stream.ReadAsync(byteArr, 0, byteArr.Length)) > 0)
|
||||
{
|
||||
await fileStream.WriteAsync(byteArr, 0, readCount);
|
||||
writeCount += readCount;
|
||||
}
|
||||
}
|
||||
return writeCount;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#region 上传无影响部分
|
||||
|
||||
/// <summary>
|
||||
/// 下载多个医生的所有附件
|
||||
/// </summary>
|
||||
/// <param name="doctorIds"></param>
|
||||
/// <returns></returns>
|
||||
|
||||
[HttpPost, Route("downloadDoctorAttachments")]
|
||||
public async Task<IResponseOutput<UploadFileInfoDTO>> DownloadAttachment(Guid[] doctorIds)
|
||||
{
|
||||
|
||||
var path = await _fileService.CreateDoctorsAllAttachmentZip(doctorIds);
|
||||
|
||||
return ResponseOutput.Ok(new UploadFileInfoDTO
|
||||
{
|
||||
FilePath = path,
|
||||
FullFilePath = path + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7)
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 下载医生官方简历
|
||||
/// </summary>
|
||||
/// <param name="language"></param>
|
||||
/// <param name="doctorIds"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost, Route("downloadOfficialCV/{language}")]
|
||||
public async Task<IResponseOutput<UploadFileInfoDTO>> DownloadOfficialResume(int language, Guid[] doctorIds)
|
||||
{
|
||||
|
||||
var path = _fileService.CreateDoctorsAllAttachmentZip(doctorIds);
|
||||
return ResponseOutput.Ok(new UploadFileInfoDTO
|
||||
{
|
||||
FilePath = await _fileService.CreateOfficialResumeZip(language, doctorIds),
|
||||
FullFilePath = path + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7)
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 下载指定医生的指定附件
|
||||
/// </summary>
|
||||
/// <param name="doctorId">医生Id</param>
|
||||
/// <param name="attachmentIds">要下载的附件Id</param>
|
||||
/// <returns></returns>
|
||||
[HttpPost, Route("downloadByAttachmentId/{doctorId}")]
|
||||
public async Task<IResponseOutput<UploadFileInfoDTO>> DownloadAttachmentById(Guid doctorId, Guid[] attachmentIds)
|
||||
{
|
||||
var path = await _fileService.CreateZipPackageByAttachment(doctorId, attachmentIds);
|
||||
return ResponseOutput.Ok(new UploadFileInfoDTO
|
||||
{
|
||||
FilePath = await _fileService.CreateZipPackageByAttachment(doctorId, attachmentIds),
|
||||
FullFilePath = path + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7)
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region 废弃
|
||||
|
||||
public class UploadDTFCommand
|
||||
{
|
||||
public Guid TrialId { get; set; }
|
||||
public Guid SiteId { get; set; }
|
||||
public Guid SubjectId { get; set; }
|
||||
public Guid SubjectVisitId { get; set; }
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// 流式上传 临时文件
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpPost("UploadDTF/{trialId:guid}/{siteId:guid}/{subjectId:guid}/{subjectVisitId:guid}/{studyId:guid}")]
|
||||
[DisableFormValueModelBinding]
|
||||
[DisableRequestSizeLimit]
|
||||
[Obsolete]
|
||||
public async Task<IResponseOutput> UploadingStream(Guid trialId, Guid siteId, Guid subjectId, Guid subjectVisitId, Guid studyId, [FromServices] IStudyDTFService studyDTFService)
|
||||
{
|
||||
|
||||
//上传根路径
|
||||
string uploadFolderPath = Path.Combine(defaultUploadFilePath, "Dicom");
|
||||
|
||||
|
||||
var dtfPath = Path.Combine(uploadFolderPath, DateTime.Now.Year.ToString(), trialId.ToString(),
|
||||
siteId.ToString(), subjectId.ToString(), subjectVisitId.ToString());
|
||||
|
||||
if (!Directory.Exists(dtfPath))
|
||||
{
|
||||
Directory.CreateDirectory(dtfPath);
|
||||
}
|
||||
|
||||
|
||||
#region 之前DTF 先上传临时文件,再拷贝
|
||||
////上传根路径
|
||||
//string uploadFolderPath = Path.Combine(defaultUploadFilePath, "UploadFile");
|
||||
////文件类型路径处理
|
||||
//var uploadTempFilePath = Path.Combine(uploadFolderPath, "TempFile");
|
||||
//if (!Directory.Exists(uploadTempFilePath)) Directory.CreateDirectory(uploadTempFilePath);
|
||||
|
||||
|
||||
//var trustedFileNameForFileStorage = Guid.NewGuid().ToString() + fileNameEX;
|
||||
|
||||
//await WriteFileAsync(section.Body, Path.Combine(uploadTempFilePath, trustedFileNameForFileStorage));
|
||||
|
||||
//var attachmentPath = $"{uploadTempFilePath}/{trustedFileNameForFileStorage}";
|
||||
|
||||
////多个文件上传,在这里返回就不合适,需要在外层,返回所有的文件路径,实际需要时再更改
|
||||
//return ResponseOutput.Ok(new UploadFileInfoDTO() { FilePath = attachmentPath });
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
//获取boundary
|
||||
var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value;
|
||||
//得到reader
|
||||
var reader = new MultipartReader(boundary, HttpContext.Request.Body);
|
||||
|
||||
var section = await reader.ReadNextSectionAsync();
|
||||
|
||||
//读取section
|
||||
while (section != null)
|
||||
{
|
||||
var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition);
|
||||
if (hasContentDispositionHeader)
|
||||
{
|
||||
var realName = contentDisposition.FileName.Value;
|
||||
|
||||
var fileNameEX = Path.GetExtension(contentDisposition.FileName.Value);
|
||||
var trustedFileNameForFileStorage = Guid.NewGuid().ToString() + fileNameEX;
|
||||
|
||||
|
||||
var relativePath = $"/Dicom/{DateTime.Now.Year.ToString()}/{trialId}/{siteId}/{subjectId}/{subjectVisitId}/{trustedFileNameForFileStorage}";
|
||||
studyDTFService.AddStudyDTF(new Core.Application.Contracts.Dicom.DTO.StudyDTFAddOrUpdateCommand()
|
||||
{
|
||||
StudyId = studyId,
|
||||
|
||||
FileName = realName,
|
||||
|
||||
Path = relativePath
|
||||
});
|
||||
|
||||
await WriteFileAsync(section.Body, Path.Combine(dtfPath, trustedFileNameForFileStorage));
|
||||
|
||||
//仅仅返回一个文件,如果多文件上传 在最后返回多个路径
|
||||
return ResponseOutput.Ok(new UploadFileInfoDTO() { FilePath = relativePath, FullFilePath = relativePath + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7) });
|
||||
}
|
||||
section = await reader.ReadNextSectionAsync();
|
||||
}
|
||||
return ResponseOutput.NotOk("Upload error");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 流式上传 非Dicom文件
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpPost("UploadNoneDICOM")]
|
||||
[DisableFormValueModelBinding]
|
||||
[DisableRequestSizeLimit]
|
||||
[Obsolete]
|
||||
public async Task<IResponseOutput> UploadingNoneDicomStream([FromForm] ArchiveStudyCommand archiveStudyCommand, [FromServices] IStudyService studyService)
|
||||
{
|
||||
//上传根路径
|
||||
string uploadFolderPath = Path.Combine(defaultUploadFilePath, "Dicom");
|
||||
|
||||
//文件类型路径处理
|
||||
//var noneDicomPath = Path.Combine(uploadFolderPath, DateTime.Now.Year.ToString(), archiveStudyCommand.TrialId.ToString(),
|
||||
// archiveStudyCommand.SiteId.ToString(), archiveStudyCommand.SubjectId.ToString(), archiveStudyCommand.SubjectVisitId.ToString(), "Data");
|
||||
|
||||
//var dtfPath = Path.Combine(uploadFolderPath, DateTime.Now.Year.ToString(), archiveStudyCommand.TrialId.ToString(),
|
||||
// archiveStudyCommand.SiteId.ToString(), archiveStudyCommand.SubjectId.ToString(), archiveStudyCommand.SubjectVisitId.ToString());
|
||||
|
||||
var noneDicomPath = string.Empty;
|
||||
|
||||
var dtfPath = string.Empty;
|
||||
|
||||
var tempDtfPath = Path.Combine(defaultUploadFilePath/*, archiveStudyCommand.DTFPath*/);
|
||||
|
||||
|
||||
if (!Directory.Exists(noneDicomPath))
|
||||
{
|
||||
Directory.CreateDirectory(noneDicomPath);
|
||||
}
|
||||
if (!System.IO.File.Exists(tempDtfPath))
|
||||
{
|
||||
return ResponseOutput.NotOk("DTF file can not found");
|
||||
}
|
||||
|
||||
System.IO.File.Move(tempDtfPath, dtfPath);
|
||||
|
||||
var saveFileDic = new Dictionary<string, string>();
|
||||
|
||||
//获取boundary
|
||||
var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value;
|
||||
//得到reader
|
||||
var reader = new MultipartReader(boundary, HttpContext.Request.Body);
|
||||
|
||||
var section = await reader.ReadNextSectionAsync();
|
||||
|
||||
//读取section
|
||||
while (section != null)
|
||||
{
|
||||
var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition);
|
||||
if (hasContentDispositionHeader)
|
||||
{
|
||||
var fileName = contentDisposition.FileName.Value;
|
||||
var fileNameEX = Path.GetExtension(contentDisposition.FileName.Value);
|
||||
var trustedFileNameForFileStorage = Guid.NewGuid() + fileNameEX;
|
||||
|
||||
|
||||
|
||||
await WriteFileAsync(section.Body, Path.Combine(noneDicomPath, trustedFileNameForFileStorage));
|
||||
|
||||
var attachmentPath = $"{noneDicomPath}/{trustedFileNameForFileStorage}";
|
||||
|
||||
saveFileDic.Add(attachmentPath, fileName);
|
||||
|
||||
|
||||
}
|
||||
section = await reader.ReadNextSectionAsync();
|
||||
}
|
||||
|
||||
//处理数据库操作
|
||||
|
||||
//studyService.DealNonDicomFile(saveFileDic, archiveStudyCommand);
|
||||
return ResponseOutput.Ok(saveFileDic);
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,303 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using IRaCIS.Application.Interfaces;
|
||||
using IRaCIS.Application.Contracts;
|
||||
using IRaCIS.Core.Application.Filter;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using System.Threading.Tasks;
|
||||
using IRaCIS.Application.Services;
|
||||
|
||||
namespace IRaCIS.Core.API.Controllers.Special
|
||||
{
|
||||
//谨慎修改 涉及到财务模块
|
||||
|
||||
[ApiController, Authorize, ApiExplorerSettings(GroupName = "Financial")]
|
||||
public class FinancialChangeController : ControllerBase
|
||||
{
|
||||
private readonly ITrialService _trialService;
|
||||
private readonly ICalculateService _calculateService;
|
||||
|
||||
public FinancialChangeController(ITrialService trialService, ICalculateService calculateService)
|
||||
{
|
||||
_trialService = trialService;
|
||||
_calculateService = calculateService;
|
||||
}
|
||||
|
||||
/// <summary> 添加实验项目-返回新增Id[AUTH]</summary>
|
||||
/// <param name="param"></param>
|
||||
/// <returns>新记录Id</returns>
|
||||
|
||||
//[TrialAudit(AuditType.TrialAudit, AuditOptType.AddOrUpdateTrial)]
|
||||
|
||||
[HttpPost, Route("trial/addOrUpdateTrial")]
|
||||
|
||||
public async Task<IResponseOutput> AddOrUpdateTrial(TrialCommand param)
|
||||
{
|
||||
var userId = Guid.Parse(User.FindFirst("id").Value);
|
||||
var result = await _trialService.AddOrUpdateTrial(param);
|
||||
|
||||
if (_trialService.TrialExpeditedChange)
|
||||
{
|
||||
var needCalReviewerIds = await _trialService.GetTrialEnrollmentReviewerIds(param.Id.Value);
|
||||
var calcList = await _calculateService.GetNeedCalculateReviewerList(Guid.Empty, string.Empty);
|
||||
|
||||
calcList.ForEach(t =>
|
||||
{
|
||||
if (needCalReviewerIds.Contains(t.DoctorId))
|
||||
{
|
||||
_calculateService.CalculateMonthlyPayment(new CalculateDoctorAndMonthDTO()
|
||||
{
|
||||
NeedCalculateReviewers = new List<Guid>()
|
||||
{
|
||||
t.DoctorId
|
||||
},
|
||||
CalculateMonth = DateTime.Parse(t.YearMonth)
|
||||
}, User.FindFirst("id").Value);
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 添加或更新工作量[AUTH]
|
||||
/// </summary>
|
||||
/// <param name="_trialWorkloadService"></param>
|
||||
/// <param name="workLoadAddOrUpdateModel"></param>
|
||||
/// <returns></returns>
|
||||
|
||||
[HttpPost, Route("doctorWorkload/workLoadAddOrUpdate")]
|
||||
[TypeFilter(typeof(TrialResourceFilter))]
|
||||
public async Task<IResponseOutput> WorkLoadAddOrUpdate([FromServices] IDoctorWorkloadService _trialWorkloadService, WorkloadCommand workLoadAddOrUpdateModel)
|
||||
{
|
||||
var userId = Guid.Parse(User.FindFirst("id").Value);
|
||||
var result = await _trialWorkloadService.AddOrUpdateWorkload(workLoadAddOrUpdateModel, userId);
|
||||
if (result.IsSuccess && workLoadAddOrUpdateModel.DataFrom == 2)
|
||||
{
|
||||
await _calculateService.CalculateMonthlyPayment(new CalculateDoctorAndMonthDTO()
|
||||
{
|
||||
NeedCalculateReviewers = new List<Guid>()
|
||||
{
|
||||
workLoadAddOrUpdateModel.DoctorId
|
||||
},
|
||||
CalculateMonth = workLoadAddOrUpdateModel.WorkTime
|
||||
}, User.FindFirst("id").Value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
[HttpDelete, Route("doctorWorkload/deleteWorkLoad/{id:guid}/{trialId:guid}")]
|
||||
[TypeFilter(typeof(TrialResourceFilter))]
|
||||
public async Task<IResponseOutput> DeleteWorkLoad([FromServices] IDoctorWorkloadService _trialWorkloadService, Guid id)
|
||||
{
|
||||
//先判断该工作量的费用是否被锁定,如果被锁定,则不能删除
|
||||
var workload = await _trialWorkloadService.GetWorkloadDetailById(id);
|
||||
var yearMonth = workload.WorkTime.ToString("yyyy-MM");
|
||||
var isLock = await _calculateService.IsLock(workload.DoctorId, yearMonth);
|
||||
|
||||
if (isLock)
|
||||
{
|
||||
return ResponseOutput.NotOk("Expenses have been settled and workload can not be reset.");
|
||||
}
|
||||
|
||||
var deleteResult = await _trialWorkloadService.DeleteWorkload(id);
|
||||
if (workload.DataFrom == (int)Domain.Share.WorkLoadFromStatus.FinalConfirm)
|
||||
{
|
||||
await _calculateService.CalculateMonthlyPayment(new CalculateDoctorAndMonthDTO()
|
||||
{
|
||||
NeedCalculateReviewers = new List<Guid>()
|
||||
{
|
||||
workload.DoctorId
|
||||
},
|
||||
CalculateMonth = workload.WorkTime
|
||||
}, User.FindFirst("id").Value);
|
||||
}
|
||||
return deleteResult;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 添加或更新汇率(会触发没有对锁定的费用计算)
|
||||
/// </summary>
|
||||
|
||||
[HttpPost, Route("exchangeRate/addOrUpdateExchangeRate")]
|
||||
public async Task<IResponseOutput> AddOrUpdateExchangeRate([FromServices] IExchangeRateService _exchangeRateService, [FromServices] IPaymentAdjustmentService _costAdjustmentService, ExchangeRateCommand addOrUpdateModel)
|
||||
{
|
||||
var result = await _exchangeRateService.AddOrUpdateExchangeRate(addOrUpdateModel);
|
||||
var calcList = await _calculateService.GetNeedCalculateReviewerList(Guid.Empty, addOrUpdateModel.YearMonth);
|
||||
foreach (var item in calcList)
|
||||
{
|
||||
if (item != null)
|
||||
{
|
||||
await _calculateService.CalculateMonthlyPayment(new CalculateDoctorAndMonthDTO()
|
||||
{
|
||||
NeedCalculateReviewers = new List<Guid>()
|
||||
{
|
||||
item.DoctorId
|
||||
},
|
||||
CalculateMonth = DateTime.Parse(item.YearMonth)
|
||||
}, User.FindFirst("id").Value);
|
||||
}
|
||||
}
|
||||
_costAdjustmentService.CalculateCNY(addOrUpdateModel.YearMonth, addOrUpdateModel.Rate);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 添加或更新 职称单价[AUTH]
|
||||
/// </summary>
|
||||
|
||||
[HttpPost, Route("rankPrice/addOrUpdateRankPrice")]
|
||||
public async Task<IResponseOutput> AddOrUpdateRankPrice([FromServices] IReviewerPayInfoService _reviewerPayInfoService, [FromServices] IRankPriceService _rankPriceService, RankPriceCommand addOrUpdateModel)
|
||||
{
|
||||
if (addOrUpdateModel.Id != Guid.Empty && addOrUpdateModel.Id != null)
|
||||
{
|
||||
var needCalReviewerIds =await _reviewerPayInfoService.GetReviewerIdByRankId(Guid.Parse(addOrUpdateModel.Id.ToString()));
|
||||
var calcList = await _calculateService.GetNeedCalculateReviewerList(Guid.Empty, string.Empty);
|
||||
|
||||
foreach (var item in calcList)
|
||||
{
|
||||
if (item != null && needCalReviewerIds.Contains(item.DoctorId))
|
||||
{
|
||||
await _calculateService.CalculateMonthlyPayment(new CalculateDoctorAndMonthDTO()
|
||||
{
|
||||
NeedCalculateReviewers = new List<Guid>()
|
||||
{
|
||||
item.DoctorId
|
||||
},
|
||||
CalculateMonth = DateTime.Parse(item.YearMonth)
|
||||
}, User.FindFirst("id").Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
var userId = Guid.Parse(User.FindFirst("id").Value);
|
||||
return await _rankPriceService.AddOrUpdateRankPrice(addOrUpdateModel, userId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 添加或更新(替换)医生支付展信息[AUTH]
|
||||
/// </summary>
|
||||
|
||||
[HttpPost, Route("reviewerPayInfo/addOrUpdateReviewerPayInfo")]
|
||||
public async Task<IResponseOutput> AddOrUpdateReviewerPayInfo([FromServices] IReviewerPayInfoService _doctorPayInfoService, ReviewerPayInfoCommand addOrUpdateModel)
|
||||
{
|
||||
var userId = Guid.Parse(User.FindFirst("id").Value);
|
||||
var result =await _doctorPayInfoService.AddOrUpdateReviewerPayInfo(addOrUpdateModel, userId);
|
||||
var calcList = await _calculateService.GetNeedCalculateReviewerList(addOrUpdateModel.DoctorId, string.Empty);
|
||||
foreach (var item in calcList)
|
||||
{
|
||||
if (item != null)
|
||||
{
|
||||
await _calculateService.CalculateMonthlyPayment(new CalculateDoctorAndMonthDTO()
|
||||
{
|
||||
NeedCalculateReviewers = new List<Guid>()
|
||||
{
|
||||
item.DoctorId
|
||||
},
|
||||
CalculateMonth = DateTime.Parse(item.YearMonth)
|
||||
}, User.FindFirst("id").Value);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 保存(替换)项目支付价格信息(会触发没有被锁定的费用计算)[AUTH]
|
||||
/// </summary>
|
||||
|
||||
[HttpPost, Route("trialPaymentPrice/addOrUpdateTrialPaymentPrice")]
|
||||
public async Task<IResponseOutput> AddOrUpdateTrialPaymentPrice([FromServices] ITrialPaymentPriceService _trialPaymentPriceService, TrialPaymentPriceCommand addOrUpdateModel)
|
||||
{
|
||||
var userId = Guid.Parse(User.FindFirst("id").Value);
|
||||
var result =await _trialPaymentPriceService.AddOrUpdateTrialPaymentPrice(addOrUpdateModel);
|
||||
var needCalReviewerIds = await _trialService.GetTrialEnrollmentReviewerIds(addOrUpdateModel.TrialId);
|
||||
var calcList = await _calculateService.GetNeedCalculateReviewerList(Guid.Empty, string.Empty);
|
||||
|
||||
foreach (var item in calcList)
|
||||
{
|
||||
if (item != null && needCalReviewerIds.Contains(item.DoctorId))
|
||||
{
|
||||
await _calculateService.CalculateMonthlyPayment(new CalculateDoctorAndMonthDTO()
|
||||
{
|
||||
NeedCalculateReviewers = new List<Guid>()
|
||||
{
|
||||
item.DoctorId
|
||||
},
|
||||
CalculateMonth = DateTime.Parse(item.YearMonth)
|
||||
}, User.FindFirst("id").Value);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量更新奖励费用[AUTH]
|
||||
/// </summary>
|
||||
|
||||
[HttpPost, Route("volumeReward/addOrUpdatevolumeRewardPriceList")]
|
||||
public async Task<IResponseOutput> AddOrUpdateAwardPriceList([FromServices] IVolumeRewardService _volumeRewardService, IEnumerable<AwardPriceCommand> addOrUpdateModel)
|
||||
{
|
||||
|
||||
var result =await _volumeRewardService.AddOrUpdateVolumeRewardPriceList(addOrUpdateModel);
|
||||
|
||||
var calcList = await _calculateService.GetNeedCalculateReviewerList(Guid.Empty, string.Empty);
|
||||
foreach (var item in calcList)
|
||||
{
|
||||
if (item != null)
|
||||
{
|
||||
await _calculateService.CalculateMonthlyPayment(new CalculateDoctorAndMonthDTO()
|
||||
{
|
||||
NeedCalculateReviewers = new List<Guid>()
|
||||
{
|
||||
item.DoctorId
|
||||
},
|
||||
CalculateMonth = DateTime.Parse(item.YearMonth)
|
||||
}, User.FindFirst("id").Value);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 计算医生月度费用,并将计算的结果存入费用表
|
||||
/// </summary>
|
||||
[HttpPost, Route("financial/calculateMonthlyPayment")]
|
||||
public async Task<IResponseOutput> CalculateMonthlyPayment(CalculateDoctorAndMonthDTO param)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return ResponseOutput.NotOk("Invalid parameter.");
|
||||
}
|
||||
return await _calculateService.CalculateMonthlyPayment(param, User.FindFirst("id").Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Financials /Monthly Payment 列表查询接口
|
||||
/// </summary>
|
||||
[HttpPost, Route("financial/getMonthlyPaymentList")]
|
||||
public async Task<IResponseOutput<PaymentDTO>> GetMonthlyPaymentList([FromServices] IPaymentService _paymentService, [FromServices] IExchangeRateService _exchangeRateService, MonthlyPaymentQueryDTO queryParam)
|
||||
{
|
||||
return ResponseOutput.Ok(new PaymentDTO
|
||||
{
|
||||
CostList = await _paymentService.GetMonthlyPaymentList(queryParam),
|
||||
ExchangeRate = await _exchangeRateService.GetExchangeRateByMonth(queryParam.StatisticsDate.ToString("yyyy-MM"))
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using AutoMapper;
|
||||
using IRaCIS.Core.Application.Contracts;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace IRaCIS.Core.API.Controllers
|
||||
{
|
||||
|
||||
[ApiController, ApiExplorerSettings(GroupName = "Reviewer")]
|
||||
public class InspectionController : ControllerBase
|
||||
{
|
||||
private readonly IRepository _repository;
|
||||
private readonly IMapper _mapper;
|
||||
private readonly IUserInfo _userInfo;
|
||||
|
||||
public InspectionController(IRepository repository, IMapper mapper, IUserInfo userInfo)
|
||||
{
|
||||
_repository = repository;
|
||||
_mapper = mapper;
|
||||
_userInfo = userInfo;
|
||||
}
|
||||
|
||||
[HttpPost, Route("trialDocument/userConfirm")]
|
||||
public async Task<IResponseOutput> UserConfirm(TrialDocumentConfirmDTO opt, [FromServices] ITrialDocumentService _trialDocumentService)
|
||||
{
|
||||
var verifyResult = await VerifySignatureAsync(opt.SignInfo);
|
||||
|
||||
if (verifyResult.IsSuccess == false)
|
||||
{
|
||||
return verifyResult;
|
||||
}
|
||||
|
||||
var bResult = await _trialDocumentService.UserConfirm(opt.OptCommand);
|
||||
|
||||
if (bResult.IsSuccess == false)
|
||||
{
|
||||
return bResult;
|
||||
}
|
||||
|
||||
//SiteId SubjectId SubjectVisitId TrialId 最开始没有 需要特殊处理
|
||||
//表冗余字段 前端只传递一次的话 后台模型就需要单独处理
|
||||
|
||||
|
||||
if (opt.AuditInfo.IsSign)
|
||||
{
|
||||
var signId = await AddSignRecordAsync(opt.SignInfo);
|
||||
|
||||
await AddInspectionRecordAsync(opt.AuditInfo, signId);
|
||||
}
|
||||
else
|
||||
{
|
||||
await AddInspectionRecordAsync(opt.AuditInfo, null);
|
||||
}
|
||||
|
||||
return bResult;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary> 验证用户签名信息 </summary> ///
|
||||
private async Task<IResponseOutput> VerifySignatureAsync(SignDTO signDTO)
|
||||
{
|
||||
var user = await _repository.FirstOrDefaultAsync<User>(u => u.UserName == signDTO.UserName && u.Password == signDTO.PassWord);
|
||||
if (user == null)
|
||||
{
|
||||
return ResponseOutput.NotOk("password error");
|
||||
}
|
||||
else if (user.Status == UserStateEnum.Disable)
|
||||
{
|
||||
return ResponseOutput.NotOk("The user has been disabled!");
|
||||
}
|
||||
return ResponseOutput.Ok();
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary> 添加签名记录 </summary> ///
|
||||
private async Task<Guid> AddSignRecordAsync(SignDTO signDTO)
|
||||
{
|
||||
var add = await _repository.AddAsync(_mapper.Map<TrialSign>(signDTO));
|
||||
|
||||
var success = await _repository.SaveChangesAsync();
|
||||
|
||||
return add.Id;
|
||||
|
||||
}
|
||||
|
||||
/// <summary> 添加稽查记录( 有的会签名,有的不会签名) </summary> ///
|
||||
private async Task AddInspectionRecordAsync(DataInspectionAddDTO addDto, Guid? signId)
|
||||
{
|
||||
|
||||
var add = await _repository.AddAsync(_mapper.Map<DataInspection>(addDto));
|
||||
|
||||
add.SignId = signId;
|
||||
add.IP = _userInfo.IP;
|
||||
|
||||
var success = await _repository.SaveChangesAsync();
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,408 @@
|
|||
using IRaCIS.Application.Interfaces;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using IRaCIS.Core.Application.Contracts.Dicom.DTO;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
using System.Threading.Tasks;
|
||||
using IRaCIS.Core.Application.Contracts.Dicom;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using IRaCIS.Core.Application.Dicom;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using IRaCIS.Core.Application.Filter;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using EasyCaching.Core;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using IRaCIS.Core.Application.Contracts;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
|
||||
namespace IRaCIS.Api.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// Study
|
||||
/// </summary>
|
||||
[Route("study")]
|
||||
[ApiController, Authorize, ApiExplorerSettings(GroupName = "Image")]
|
||||
public class StudyController : ControllerBase
|
||||
{
|
||||
private readonly IStudyService _studyService;
|
||||
private readonly IDicomArchiveService _dicomArchiveService;
|
||||
private readonly ILogger<StudyController> _logger;
|
||||
|
||||
private IEasyCachingProvider _provider;
|
||||
private IUserInfo _userInfo;
|
||||
private static object _locker = new object();
|
||||
|
||||
|
||||
public StudyController(IStudyService studyService,
|
||||
IDicomArchiveService dicomArchiveService,
|
||||
ILogger<StudyController> logger,
|
||||
IEasyCachingProvider provider, IUserInfo userInfo
|
||||
)
|
||||
{
|
||||
_userInfo = userInfo;
|
||||
_provider = provider;
|
||||
_studyService = studyService;
|
||||
_dicomArchiveService = dicomArchiveService;
|
||||
_logger = logger;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary> 归档</summary>
|
||||
[HttpPost, Route("archiveStudy/{trialId:guid}")]
|
||||
[DisableFormValueModelBinding]
|
||||
[DisableRequestSizeLimit]
|
||||
[TypeFilter(typeof(TrialResourceFilter))]
|
||||
public async Task<IResponseOutput> ArchiveStudy([FromForm] ArchiveStudyCommand archiveStudyCommand)
|
||||
{
|
||||
//Stopwatch sw = new Stopwatch();
|
||||
var startTime = DateTime.Now;
|
||||
//sw.Start();
|
||||
|
||||
if (_provider.Exists("StudyUid_" + archiveStudyCommand.StudyInstanceUid))
|
||||
{
|
||||
return ResponseOutput.NotOk("当前已有人正在上传和归档该检查!");
|
||||
}
|
||||
else
|
||||
{
|
||||
_provider.Set("StudyUid_" + archiveStudyCommand.StudyInstanceUid, _userInfo.Id, TimeSpan.FromMinutes(30));
|
||||
}
|
||||
|
||||
var archiveResult = new DicomArchiveResult();
|
||||
var archivedStudyIds = new List<Guid>();
|
||||
var seriesInstanceUidList = new List<string>();
|
||||
var instanceUidList = new List<string>();
|
||||
|
||||
//重传的时候,找出当前检查已经上传的series instance
|
||||
if (archiveStudyCommand.AbandonStudyId != null)
|
||||
{
|
||||
_studyService.GetHasUploadSeriesAndInstance(archiveStudyCommand.AbandonStudyId.Value, ref seriesInstanceUidList, ref instanceUidList);
|
||||
}
|
||||
|
||||
var savedInfo = _studyService.GetSaveToDicomInfo(archiveStudyCommand.SubjectVisitId);
|
||||
|
||||
|
||||
var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value;
|
||||
var reader = new MultipartReader(boundary, HttpContext.Request.Body);
|
||||
var section = await reader.ReadNextSectionAsync();
|
||||
while (section != null)
|
||||
{
|
||||
//采用post方式 这里多加一个判断 过滤其他参数
|
||||
if (string.IsNullOrEmpty(section.ContentType))
|
||||
{
|
||||
section = await reader.ReadNextSectionAsync();
|
||||
continue;
|
||||
}
|
||||
|
||||
var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition);
|
||||
if (hasContentDispositionHeader)
|
||||
{
|
||||
string fileName = contentDisposition.FileName.Value;
|
||||
try
|
||||
{
|
||||
|
||||
string mediaType = section.ContentType;
|
||||
|
||||
if (mediaType.Contains("zip"))
|
||||
{
|
||||
var partStream = section.Body;
|
||||
using (var zipArchive = new ZipArchive(partStream, ZipArchiveMode.Read))
|
||||
{
|
||||
foreach (var entry in zipArchive.Entries)
|
||||
{
|
||||
if (entry.FullName.EndsWith("/")) continue;
|
||||
try
|
||||
{
|
||||
++archiveResult.ReceivedFileCount;
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
await section.Body.CopyToAsync(memoryStream);
|
||||
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
var archiveStudyId = await _dicomArchiveService.ArchiveDicomStreamAsync(memoryStream, savedInfo, seriesInstanceUidList, instanceUidList);
|
||||
|
||||
if (!archivedStudyIds.Contains(archiveStudyId))
|
||||
archivedStudyIds.Add(archiveStudyId);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
archiveResult.ErrorFiles.Add($"{fileName}/{entry.FullName}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
++archiveResult.ReceivedFileCount;
|
||||
|
||||
|
||||
if (mediaType.Contains("octet-stream"))
|
||||
{
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
await section.Body.CopyToAsync(memoryStream);
|
||||
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
var archiveStudyId = await _dicomArchiveService.ArchiveDicomStreamAsync(memoryStream, savedInfo, seriesInstanceUidList, instanceUidList);
|
||||
if (!archivedStudyIds.Contains(archiveStudyId))
|
||||
archivedStudyIds.Add(archiveStudyId);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e.Message + e.StackTrace);
|
||||
|
||||
archiveResult.ErrorFiles.Add(fileName);
|
||||
|
||||
_provider.Remove("StudyUid_" + archiveStudyCommand.StudyInstanceUid);
|
||||
|
||||
}
|
||||
}
|
||||
section = await reader.ReadNextSectionAsync();
|
||||
}
|
||||
|
||||
if (archivedStudyIds.Count > 0) // 上传成功,处理逻辑
|
||||
{
|
||||
|
||||
|
||||
// 同一个访视 多个线程上传处理 批量保存 可能造成死锁 https://www.cnblogs.com/johnblogs/p/9945767.html
|
||||
|
||||
await _dicomArchiveService.DicomDBDataSaveChange();
|
||||
|
||||
//sw.Stop();
|
||||
_studyService.UploadOrReUploadNeedTodo(archiveStudyCommand, archivedStudyIds, ref archiveResult, new StudyMonitor()
|
||||
{
|
||||
TrialId = savedInfo.TrialId,
|
||||
SiteId = savedInfo.SiteId,
|
||||
SubjectId = savedInfo.SubjectId,
|
||||
SubjectVisitId = savedInfo.SubjectVisitId,
|
||||
StudyId = archivedStudyIds[0],
|
||||
UploadStartTime = startTime,
|
||||
UploadFinishedTime = DateTime.Now,
|
||||
FileSize = (decimal)HttpContext.Request.ContentLength,
|
||||
FileCount = archiveResult.ReceivedFileCount,
|
||||
IsDicom = true,
|
||||
IsDicomReUpload = archiveStudyCommand.AbandonStudyId!=null,
|
||||
IP =_userInfo.IP
|
||||
});
|
||||
|
||||
_provider.Remove("StudyUid_" + archiveStudyCommand.StudyInstanceUid);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
return ResponseOutput.NotOk("未完成该检查的归档", archiveResult);
|
||||
}
|
||||
|
||||
return ResponseOutput.Ok(archiveResult);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#region 2021.12.14 整理废弃
|
||||
|
||||
//[Obsolete]
|
||||
//[HttpGet, Route("forwardStudy/{studyId:guid}/{trialId:guid}")]
|
||||
//[TrialAudit(AuditType.StudyAudit, AuditOptType.Forwarded)]
|
||||
//[TypeFilter(typeof(TrialResourceFilter))]
|
||||
|
||||
//public IResponseOutput ForwardStudy(Guid studyId)
|
||||
//{
|
||||
// return _studyService.ForwardStudy(studyId);
|
||||
//}
|
||||
|
||||
///// <summary> 指定资源Id,获取Dicom检查信息 </summary>
|
||||
///// <param name="studyId"> Dicom检查的Id </param>
|
||||
//[HttpGet, Route("item/{studyId:guid}")]
|
||||
//[Obsolete]
|
||||
//public IResponseOutput<DicomStudyDTO> GetStudyItem(Guid studyId)
|
||||
//{
|
||||
// return ResponseOutput.Ok(_studyService.GetStudyItem(studyId));
|
||||
//}
|
||||
|
||||
//[Obsolete]
|
||||
//[HttpDelete, Route("deleteStudy/{id:guid}/{trialId:guid}")]
|
||||
//public IResponseOutput DeleteStudy(Guid id)
|
||||
//{
|
||||
// return _studyService.DeleteStudy(id);
|
||||
//}
|
||||
|
||||
//[Obsolete]
|
||||
//[HttpPost, Route("getStudyList")]
|
||||
//public IResponseOutput<PageOutput<StudyDTO>> GetStudyList(StudyQueryDTO queryDto)
|
||||
//{
|
||||
// return ResponseOutput.Ok(_studyService.GetStudyList(queryDto));
|
||||
//}
|
||||
|
||||
|
||||
/////// <summary> 指定资源Id,渲染Dicom检查的Jpeg预览图像 </summary>
|
||||
/////// <param name="studyId"> Dicom检查的Id </param>
|
||||
////[HttpGet, Route("preview/{studyId:guid}")]
|
||||
////[AllowAnonymous]
|
||||
////public FileContentResult GetStudyPreview(Guid studyId)
|
||||
////{
|
||||
//// string path = _studyService.GetStudyPreview(studyId);
|
||||
//// using (var sw = DicomRenderingHelper.RenderPreviewJpeg(path))
|
||||
//// {
|
||||
//// var bytes = new byte[sw.Length];
|
||||
//// sw.Read(bytes, 0, bytes.Length);
|
||||
//// sw.Close();
|
||||
//// return new FileContentResult(bytes, "image/jpeg");
|
||||
//// }
|
||||
////}
|
||||
|
||||
///// <summary>
|
||||
///// Dicom匿名化
|
||||
///// </summary>
|
||||
///// <param name="studyId">需要匿名化的检查Id</param>
|
||||
///// <returns></returns>
|
||||
//[HttpPost, Route("dicomAnonymize/{studyId:guid}/{trialId:guid}")]
|
||||
//[TrialAudit(AuditType.StudyAudit, AuditOptType.Anonymized)]
|
||||
//[Obsolete]
|
||||
//[TypeFilter(typeof(TrialResourceFilter))]
|
||||
//public async Task<IResponseOutput> DicomAnonymize(Guid studyId)
|
||||
//{
|
||||
// string userName = User.FindFirst("realName").Value; ;
|
||||
// return await _studyService.DicomAnonymize(studyId, userName);
|
||||
//}
|
||||
|
||||
|
||||
///// <summary>
|
||||
///// 获取受试者 这次访视 对应的study modality 列表
|
||||
///// </summary>
|
||||
///// <param name="trialId"></param>
|
||||
///// <param name="siteId"></param>
|
||||
///// <param name="subjectId"></param>
|
||||
///// <param name="subjectVisitId"></param>
|
||||
///// <returns></returns>
|
||||
//[HttpPost, Route("getSubjectVisitStudyList/{trialId:guid}/{siteId:guid}/{subjectId:guid}/{subjectVisitId:guid}")]
|
||||
//[Obsolete]
|
||||
//[AllowAnonymous]
|
||||
//public IResponseOutput<List<SubjectVisitStudyDTO>> GetSubjectVisitStudyList(Guid trialId, Guid siteId, Guid subjectId,
|
||||
// Guid subjectVisitId)
|
||||
//{
|
||||
// return ResponseOutput.Ok(_studyService.GetSubjectVisitStudyList(trialId, siteId, subjectId, subjectVisitId));
|
||||
//}
|
||||
|
||||
//[HttpPost, Route("getDistributeStudyList")]
|
||||
//[Obsolete]
|
||||
//public IResponseOutput<PageOutput<DistributeReviewerStudyStatusDTO>> GetDistributeStudyList(StudyStatusQueryDTO studyStatusQueryDto)
|
||||
//{
|
||||
// return ResponseOutput.Ok(_studyService.GetDistributeStudyList(studyStatusQueryDto));
|
||||
//}
|
||||
|
||||
/////// <summary> 删除检查</summary>
|
||||
////[HttpDelete, Route("deleteStudy/{id:guid}/{trialId:guid}")]
|
||||
////
|
||||
////[TypeFilter(typeof(TrialResourceFilter))]
|
||||
////public IResponseOutput DeleteStudy(Guid id)
|
||||
////{
|
||||
//// return _studyService.DeleteStudy(id);
|
||||
////}
|
||||
|
||||
///// <summary> 更新Study状态,并保存状态变更信息 </summary>
|
||||
///// <param name="studyStatusDetailCommand"></param>
|
||||
|
||||
//[HttpPost, Route("updateStudyStatus/{trialId:guid}")]
|
||||
|
||||
//[TrialAudit(AuditType.StudyAudit, AuditOptType.ChangeStudyStatus)]
|
||||
//[Obsolete]
|
||||
//[TypeFilter(typeof(TrialResourceFilter))]
|
||||
//public IResponseOutput UpdateStudyStatus(StudyStatusDetailCommand studyStatusDetailCommand)
|
||||
//{
|
||||
// return _studyService.UpdateStudyStatus(studyStatusDetailCommand);
|
||||
//}
|
||||
|
||||
///// <summary>
|
||||
///// 根据项目Id 获取可选医生列表
|
||||
///// </summary>
|
||||
///// <param name="trialId"></param>
|
||||
///// <returns></returns>
|
||||
//[HttpGet, Route("GetReviewerList/{trialId:guid}")]
|
||||
//[Obsolete]
|
||||
//public IResponseOutput<List<ReviewerDistributionDTO>> GetReviewerListByTrialId(Guid trialId)
|
||||
//{
|
||||
// var result = _studyService.GetReviewerListByTrialId(trialId);
|
||||
// return ResponseOutput.Ok(result);
|
||||
//}
|
||||
|
||||
///// <summary>
|
||||
///// 根据StudyId获取该Study的操作记录,时间倒序
|
||||
///// </summary>
|
||||
///// <param name="studyId"></param>
|
||||
///// <returns></returns>
|
||||
//[HttpGet, Route("getStudyStatusDetailList/{studyId:guid}")]
|
||||
//[Obsolete]
|
||||
//public IResponseOutput<List<StudyStatusDetailDTO>> GetStudyStatusDetailList(Guid studyId)
|
||||
//{
|
||||
// var result = _studyService.GetStudyStatusDetailList(studyId);
|
||||
// return ResponseOutput.Ok(result);
|
||||
//}
|
||||
|
||||
///// <summary>
|
||||
///// 获取某个访视的关联访视
|
||||
///// 用于获取关联影像(调用之前的接口:/series/list/,根据StudyId,获取访视的序列列表)
|
||||
///// </summary>
|
||||
///// <param name="visitNum"></param>
|
||||
///// <param name="tpCode"></param>
|
||||
///// <returns></returns>
|
||||
//[HttpGet, Route("getRelationVisitList/{visitNum}/{tpCode}")]
|
||||
//[Obsolete]
|
||||
//[AllowAnonymous]
|
||||
//public IResponseOutput<IEnumerable<RelationVisitDTO>> GetRelationVisitList(decimal visitNum, string tpCode)
|
||||
//{
|
||||
// return ResponseOutput.Ok(_studyService.GetRelationVisitList(visitNum, tpCode));
|
||||
//}
|
||||
|
||||
///// <summary>
|
||||
///// 保存标记(跟删除合并,每次保存最新的标记),会删除替换之前的标记
|
||||
///// 外层的TPcode 必须传,里面的标记数组可为空数组,表示删除该Study的所有标记
|
||||
///// </summary>
|
||||
///// <param name="imageLabelCommand"></param>
|
||||
///// <returns></returns>
|
||||
//[HttpPost, Route("saveImageLabelList")]
|
||||
//[AllowAnonymous]
|
||||
//[Obsolete]
|
||||
|
||||
//public IResponseOutput SaveImageLabelList(ImageLabelCommand imageLabelCommand)
|
||||
//{
|
||||
// return ResponseOutput.Result(_studyService.SaveImageLabelList(imageLabelCommand));
|
||||
//}
|
||||
|
||||
///// <summary>
|
||||
///// 根据TPCode 获取所有的标记
|
||||
///// </summary>
|
||||
///// <param name="tpCode"></param>
|
||||
///// <returns></returns>
|
||||
//[HttpGet, Route("getImageLabelList/{tpCode}")]
|
||||
//[Obsolete]
|
||||
//[AllowAnonymous]
|
||||
//public IResponseOutput<IEnumerable<ImageLabelDTO>> GetImageLabelList(string tpCode)
|
||||
//{
|
||||
// return ResponseOutput.Ok(_studyService.GetImageLabel(tpCode));
|
||||
//}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,280 @@
|
|||
using AutoMapper;
|
||||
using IRaCIS.Core.Application.Contracts;
|
||||
using IRaCIS.Core.Application.Filter;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IRaCIS.Core.API.Controllers
|
||||
{
|
||||
[ApiExplorerSettings(GroupName = "Trial")]
|
||||
[ApiController]
|
||||
public class UploadController : ControllerBase
|
||||
{
|
||||
public IMapper _mapper { get; set; }
|
||||
public IUserInfo _userInfo { get; set; }
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
private readonly IWebHostEnvironment _hostEnvironment;
|
||||
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
|
||||
public UploadController(IMapper mapper, IUserInfo userInfo, IMediator mediator, IWebHostEnvironment hostEnvironment, IServiceProvider serviceProvider)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_hostEnvironment = hostEnvironment;
|
||||
_mediator = mediator;
|
||||
_mapper = mapper;
|
||||
_userInfo = userInfo;
|
||||
}
|
||||
|
||||
[HttpPost("TrialDocument/UploadTrialDoc/{trialId:guid}/{type}")]
|
||||
[DisableRequestSizeLimit]
|
||||
[DisableFormValueModelBinding]
|
||||
public async Task<IResponseOutput> UploadTrialDoc(Guid trialId,string type)
|
||||
{
|
||||
var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value;
|
||||
|
||||
var reader = new MultipartReader(boundary, HttpContext.Request.Body);
|
||||
|
||||
var section = await reader.ReadNextSectionAsync();
|
||||
|
||||
while (section != null)
|
||||
{
|
||||
var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition);
|
||||
|
||||
if (hasContentDispositionHeader)
|
||||
{
|
||||
|
||||
DealTrialStorePath(trialId, type, contentDisposition.FileName.Value, out string serverFilePath, out string relativePath );
|
||||
|
||||
await WriteFileAsync(section.Body, serverFilePath);
|
||||
|
||||
//仅仅返回一个文件,如果多文件上传 在最后返回多个路径
|
||||
return ResponseOutput.Ok(new
|
||||
{
|
||||
FilePath = relativePath,
|
||||
FullFilePath = relativePath + "?access_token=" + _userInfo.UserToken
|
||||
});
|
||||
|
||||
}
|
||||
section = await reader.ReadNextSectionAsync();
|
||||
}
|
||||
return ResponseOutput.Ok();
|
||||
}
|
||||
|
||||
|
||||
[HttpPost("TrialDocument/UploadSystemDoc/{type}")]
|
||||
[DisableRequestSizeLimit]
|
||||
[DisableFormValueModelBinding]
|
||||
public async Task<IResponseOutput> UploadSysTemDoc( string type)
|
||||
{
|
||||
var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value;
|
||||
|
||||
var reader = new MultipartReader(boundary, HttpContext.Request.Body);
|
||||
|
||||
var section = await reader.ReadNextSectionAsync();
|
||||
|
||||
while (section != null)
|
||||
{
|
||||
var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition);
|
||||
|
||||
if (hasContentDispositionHeader)
|
||||
{
|
||||
|
||||
DealSysTemStorePath( type, contentDisposition.FileName.Value, out string serverFilePath, out string relativePath);
|
||||
|
||||
await WriteFileAsync(section.Body, serverFilePath);
|
||||
|
||||
//仅仅返回一个文件,如果多文件上传 在最后返回多个路径
|
||||
return ResponseOutput.Ok(new
|
||||
{
|
||||
FilePath = relativePath,
|
||||
FullFilePath = relativePath + "?access_token=" + _userInfo.UserToken
|
||||
});
|
||||
|
||||
}
|
||||
section = await reader.ReadNextSectionAsync();
|
||||
}
|
||||
return ResponseOutput.Ok();
|
||||
}
|
||||
|
||||
private void DealSysTemStorePath( string type, string fileRealName, out string serverFilePath, out string relativePath)
|
||||
{
|
||||
var rootPath = Directory.GetParent(_hostEnvironment.ContentRootPath.TrimEnd('\\')).FullName;
|
||||
//上传根路径
|
||||
var _fileStorePath = Path.Combine(rootPath, StaticData.TrialDataFolder);
|
||||
|
||||
//文件类型路径处理
|
||||
var uploadFolderPath = Path.Combine(_fileStorePath, "SysTemDocument", type);
|
||||
if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath);
|
||||
|
||||
|
||||
var fileNameEX = Path.GetExtension(fileRealName);
|
||||
var trustedFileNameForFileStorage = Guid.NewGuid().ToString() + fileNameEX;
|
||||
|
||||
relativePath = $"/{StaticData.TrialDataFolder}/SysTemDocument/{type}/{trustedFileNameForFileStorage}";
|
||||
|
||||
serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage);
|
||||
}
|
||||
|
||||
|
||||
private void DealTrialStorePath(Guid trialId,string type,string fileRealName, out string serverFilePath, out string relativePath)
|
||||
{
|
||||
var rootPath = Directory.GetParent(_hostEnvironment.ContentRootPath.TrimEnd('\\')).FullName;
|
||||
//上传根路径
|
||||
var _fileStorePath = Path.Combine(rootPath, StaticData.TrialDataFolder);
|
||||
|
||||
//文件类型路径处理
|
||||
var uploadFolderPath = Path.Combine(_fileStorePath, trialId.ToString(), type);
|
||||
if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath);
|
||||
|
||||
|
||||
var fileNameEX = Path.GetExtension(fileRealName);
|
||||
var trustedFileNameForFileStorage = Guid.NewGuid().ToString() + fileNameEX;
|
||||
|
||||
relativePath = $"/{StaticData.TrialDataFolder}/{trialId}/{type}/{trustedFileNameForFileStorage}";
|
||||
|
||||
serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 上传临床数据
|
||||
/// </summary>
|
||||
/// <param name="subjectVisitId"></param>
|
||||
/// <param name="uploadType"> 1:DICOM DTF 2:非DIOM DTF 3: 受试者临床数据</param>
|
||||
/// <param name="_subjectVisitRepository"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("ClinicalData/UploadVisitClinicalData/{trialId:guid}/{subjectVisitId:guid}/{type}")]
|
||||
[DisableRequestSizeLimit]
|
||||
[DisableFormValueModelBinding]
|
||||
[Obsolete]
|
||||
public async Task<IResponseOutput> UploadVisitData(Guid subjectVisitId, [FromRoute] UploadFileTypeEnum uploadType, [FromServices] IRepository<SubjectVisit> _subjectVisitRepository)
|
||||
{
|
||||
|
||||
var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value;
|
||||
|
||||
var reader = new MultipartReader(boundary, HttpContext.Request.Body);
|
||||
|
||||
var section = await reader.ReadNextSectionAsync();
|
||||
while (section != null)
|
||||
{
|
||||
var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition);
|
||||
|
||||
if (hasContentDispositionHeader)
|
||||
{
|
||||
|
||||
DealStorePath(subjectVisitId, contentDisposition.FileName.Value, uploadType, out string serverFilePath, out string relativePath, _subjectVisitRepository);
|
||||
|
||||
await WriteFileAsync(section.Body, serverFilePath);
|
||||
|
||||
//仅仅返回一个文件,如果多文件上传 在最后返回多个路径
|
||||
return ResponseOutput.Ok(new
|
||||
{
|
||||
FilePath = relativePath,
|
||||
FullFilePath = relativePath + "?access_token=" + _userInfo.UserToken
|
||||
});
|
||||
|
||||
}
|
||||
section = await reader.ReadNextSectionAsync();
|
||||
}
|
||||
return ResponseOutput.Ok();
|
||||
|
||||
}
|
||||
|
||||
public enum UploadFileTypeEnum
|
||||
{
|
||||
DICOM_DTF = 1,
|
||||
|
||||
NonDICOM_DTF = 2,
|
||||
|
||||
SubjectTreatement = 3
|
||||
}
|
||||
|
||||
private void DealStorePath(Guid subjectVisitId, string realName, UploadFileTypeEnum typeEnum, out string serverFilePath, out string relativePath, IRepository<SubjectVisit> _subjectVisitRepository)
|
||||
{
|
||||
|
||||
var rootPath = Directory.GetParent(_hostEnvironment.ContentRootPath.TrimEnd('\\')).FullName;
|
||||
|
||||
//上传根路径
|
||||
var _fileStorePath = Path.Combine(rootPath, StaticData.TrialDataFolder);
|
||||
|
||||
var sv = _subjectVisitRepository.Where(t => t.Id == subjectVisitId).Select(t => new { t.TrialId, t.SiteId, t.SubjectId }).FirstOrDefault();
|
||||
|
||||
//处理存储的文件夹
|
||||
var typeFolder = typeEnum == UploadFileTypeEnum.SubjectTreatement ? StaticData.TreatmenthistoryFolder
|
||||
: typeEnum == UploadFileTypeEnum.NonDICOM_DTF ? StaticData.NoneDicomFolder
|
||||
: /*typeEnum == UploadFileTypeEnum.DICOM_DTF ?*/ StaticData.DicomFolder;
|
||||
|
||||
string uploadFolderPath = Path.Combine(_fileStorePath, sv.TrialId.ToString(), sv.SiteId.ToString(), sv.SubjectId.ToString(), subjectVisitId.ToString(), typeFolder);
|
||||
|
||||
if (!Directory.Exists(uploadFolderPath))
|
||||
{
|
||||
Directory.CreateDirectory(uploadFolderPath);
|
||||
}
|
||||
|
||||
var fileNameEX = Path.GetExtension(realName);
|
||||
|
||||
var trustedFileNameForFileStorage = Guid.NewGuid().ToString() + fileNameEX;
|
||||
|
||||
relativePath = $"/{StaticData.TrialDataFolder}/{sv.TrialId}/{sv.SiteId}/{sv.SubjectId}/{subjectVisitId}/{typeFolder}/{trustedFileNameForFileStorage}";
|
||||
|
||||
serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage);
|
||||
|
||||
//处理 是上传文件返回路径 还是上传文件后,需要保存数据库
|
||||
|
||||
|
||||
if (typeEnum == UploadFileTypeEnum.SubjectTreatement)
|
||||
{
|
||||
var repository = _serviceProvider.GetService(typeof(IRepository<PreviousPDF>)) as IRepository<PreviousPDF>;
|
||||
|
||||
_= repository.InsertOrUpdateAsync(new PreviousPDFAddOrEdit() { FileName = realName, Path = relativePath, SubjectVisitId = subjectVisitId }, true).Result;
|
||||
}
|
||||
else if (typeEnum == UploadFileTypeEnum.NonDICOM_DTF)
|
||||
{
|
||||
|
||||
}
|
||||
else if (typeEnum == UploadFileTypeEnum.NonDICOM_DTF)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写文件导到磁盘
|
||||
/// </summary>
|
||||
/// <param name="stream">流</param>
|
||||
/// <param name="path">文件保存路径</param>
|
||||
/// <returns></returns>
|
||||
private static async Task<int> WriteFileAsync(System.IO.Stream stream, string path)
|
||||
{
|
||||
const int FILE_WRITE_SIZE = 84975;//写出缓冲区大小
|
||||
int writeCount = 0;
|
||||
using (FileStream fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Write, FILE_WRITE_SIZE, true))
|
||||
{
|
||||
byte[] byteArr = new byte[FILE_WRITE_SIZE];
|
||||
int readCount = 0;
|
||||
while ((readCount = await stream.ReadAsync(byteArr, 0, byteArr.Length)) > 0)
|
||||
{
|
||||
await fileStream.WriteAsync(byteArr, 0, readCount);
|
||||
writeCount += readCount;
|
||||
}
|
||||
}
|
||||
return writeCount;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
// 使用的是proto3版本
|
||||
syntax = "proto3";
|
||||
// 定义命名空间,后续生成代码时就会生成对应的命名空间
|
||||
option csharp_namespace = "gRPC.ZHiZHUN.AuthServer.protos";
|
||||
|
||||
/*
|
||||
每一句需要用分号结尾
|
||||
message 用来定义请求和返回数据格式
|
||||
tag message后面的值数字代表是字段的标识(tag),不是赋值,
|
||||
*/
|
||||
|
||||
|
||||
// 新增用户时需要传递数据消息, 可理解为一个类
|
||||
message GetTokenReuqest{
|
||||
string id=1;
|
||||
string userName=2;
|
||||
string realName=3;
|
||||
string reviewerCode=4;
|
||||
int32 userTypeEnumInt=5;
|
||||
string userTypeShortName=6;
|
||||
bool isAdmin=7;
|
||||
|
||||
}
|
||||
|
||||
// 新增时返回的消息格式
|
||||
message GetTokenResponse {
|
||||
int32 code=1;
|
||||
string token =2;
|
||||
}
|
||||
|
||||
|
||||
// service 用标识定义服务的,里面写对应的方法
|
||||
service TokenGrpcService{
|
||||
// 获取token
|
||||
rpc GetUserToken(GetTokenReuqest) returns (GetTokenResponse);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
// 新增用户时需要传递数据消息, 可理解为一个类
|
||||
message AddUserReuqest{
|
||||
string name=1;
|
||||
int32 age=2;
|
||||
bool isBoy=3;
|
||||
}
|
||||
// 新增时返回的消息格式
|
||||
message ResultResponse {
|
||||
int32 code=1;
|
||||
string msg =2;
|
||||
}
|
||||
//传递的查询条件信息格式,可理解为平时传入的查询条件对象
|
||||
message QueryUserReuqest{
|
||||
string name=1;
|
||||
}
|
||||
//查询返回的用户信息格式,可理解为返回的类
|
||||
message UserInfoResponse {
|
||||
string name=1;
|
||||
int32 age=2;
|
||||
string gender=3;
|
||||
}
|
||||
|
||||
// service 用标识定义服务的,里面写对应的方法
|
||||
service UserService{
|
||||
// 新增用户
|
||||
rpc AddUser(AddUserReuqest) returns (ResultResponse);
|
||||
// 查询用户
|
||||
rpc GetAllUser(QueryUserReuqest) returns (UserInfoResponse);
|
||||
}
|
||||
*/
|
|
@ -0,0 +1,107 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<SignAssembly>false</SignAssembly>
|
||||
<UserSecretsId>354572d4-9e15-4099-807c-63a2d29ff9f2</UserSecretsId>
|
||||
<LangVersion>default</LangVersion>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DocumentationFile>.\IRaCIS.Core.API.xml</DocumentationFile>
|
||||
<NoWarn>1701;1702;1591;</NoWarn>
|
||||
<OutputPath>..\bin\</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<DocumentationFile>bin\Release\IRaCIS.Core.API.xml</DocumentationFile>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<NoWarn>1701;1702;1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Controllers\ReviewerApi\**" />
|
||||
<Compile Remove="UploadFile\**" />
|
||||
<Content Remove="Controllers\ReviewerApi\**" />
|
||||
<Content Remove="UploadFile\**" />
|
||||
<EmbeddedResource Remove="Controllers\ReviewerApi\**" />
|
||||
<EmbeddedResource Remove="UploadFile\**" />
|
||||
<None Remove="Controllers\ReviewerApi\**" />
|
||||
<None Remove="UploadFile\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Remove="web.config" />
|
||||
<Content Remove="wwwroot\swagger\ui\abp.js" />
|
||||
<Content Remove="wwwroot\swagger\ui\abp.swagger.js" />
|
||||
<Content Remove="wwwroot\swagger\ui\Index.html" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove=".preview.jpg" />
|
||||
<None Remove="GrpcToken.proto" />
|
||||
<None Remove="IRaCIS.Core.API.xml" />
|
||||
<None Remove="Protos\GrpcToken.proto" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ApplicationDefinition Include="GrpcToken.proto" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="wwwroot\swagger\ui\abp.js" />
|
||||
<EmbeddedResource Include="wwwroot\swagger\ui\abp.swagger.js" />
|
||||
<EmbeddedResource Include="wwwroot\swagger\ui\Index.html" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Protobuf Include="Protos\GrpcToken.proto">
|
||||
<GrpcServices>Client</GrpcServices>
|
||||
</Protobuf>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AspNetCoreRateLimit" Version="4.0.1" />
|
||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="7.2.0" />
|
||||
<PackageReference Include="EasyCaching.InMemory" Version="1.4.1" />
|
||||
<PackageReference Include="EasyCaching.Interceptor.Castle" Version="1.4.1" />
|
||||
<PackageReference Include="Google.Protobuf" Version="3.19.1" />
|
||||
<PackageReference Include="Grpc.Net.Client" Version="2.41.0" />
|
||||
<PackageReference Include="Grpc.Tools" Version="2.42.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Hangfire.Tags.SqlServer" Version="1.8.0" />
|
||||
<PackageReference Include="Invio.Extensions.Authentication.JwtBearer" Version="2.0.1" />
|
||||
<PackageReference Include="LogDashboard" Version="1.4.8" />
|
||||
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.14.0" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="6.0.1" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="4.1.0" />
|
||||
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="1.1.4" />
|
||||
<PackageReference Include="Serilog.Sinks.Email" Version="2.4.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\IRaCIS.Core.Application\IRaCIS.Core.Application.csproj" />
|
||||
<ProjectReference Include="..\IRaCIS.Core.Infra.EFCore\IRaCIS.Core.Infra.EFCore.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Properties\PublishProfiles\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Update="NLog.config">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ProjectExtensions><VisualStudio><UserProperties anonymizetagsetting_1json__JsonSchema="http://json.schemastore.org/jovo-language-model" /></VisualStudio></ProjectExtensions>
|
||||
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,110 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<SignAssembly>false</SignAssembly>
|
||||
<UserSecretsId>354572d4-9e15-4099-807c-63a2d29ff9f2</UserSecretsId>
|
||||
<LangVersion>default</LangVersion>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DocumentationFile>.\IRaCIS.Core.API.xml</DocumentationFile>
|
||||
<NoWarn>1701;1702;1591;</NoWarn>
|
||||
<OutputPath>..\bin\</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<DocumentationFile>bin\Release\IRaCIS.Core.API.xml</DocumentationFile>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<NoWarn>1701;1702;1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="UploadFile\**" />
|
||||
<Compile Remove="wwwroot\**" />
|
||||
<Content Remove="UploadFile\**" />
|
||||
|
||||
<EmbeddedResource Remove="UploadFile\**" />
|
||||
<EmbeddedResource Remove="wwwroot\**" />
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Remove="web.config" />
|
||||
<Content Remove="wwwroot\swagger\ui\abp.js" />
|
||||
<Content Remove="wwwroot\swagger\ui\abp.swagger.js" />
|
||||
<Content Remove="wwwroot\swagger\ui\Index.html" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove=".preview.jpg" />
|
||||
<None Remove="GrpcToken.proto" />
|
||||
<None Remove="IRaCIS.Core.API.xml" />
|
||||
<None Remove="Protos\GrpcToken.proto" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ApplicationDefinition Include="GrpcToken.proto" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="wwwroot\swagger\ui\abp.js">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="wwwroot\swagger\ui\abp.swagger.js">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="wwwroot\swagger\ui\Index.html">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Protobuf Include="Protos\GrpcToken.proto">
|
||||
<GrpcServices>Client</GrpcServices>
|
||||
</Protobuf>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AspNetCoreRateLimit" Version="4.0.1" />
|
||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="7.2.0" />
|
||||
<PackageReference Include="EasyCaching.InMemory" Version="1.4.1" />
|
||||
<PackageReference Include="EasyCaching.Interceptor.Castle" Version="1.4.1" />
|
||||
<PackageReference Include="Google.Protobuf" Version="3.19.1" />
|
||||
<PackageReference Include="Grpc.Net.Client" Version="2.41.0" />
|
||||
<PackageReference Include="Grpc.Tools" Version="2.42.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Hangfire.Tags.SqlServer" Version="1.8.0" />
|
||||
<PackageReference Include="Invio.Extensions.Authentication.JwtBearer" Version="2.0.1" />
|
||||
<PackageReference Include="LogDashboard" Version="1.4.8" />
|
||||
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.14.0" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="6.0.1" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="4.1.0" />
|
||||
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="1.1.4" />
|
||||
<PackageReference Include="Serilog.Sinks.Email" Version="2.4.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\IRaCIS.Core.Application\IRaCIS.Core.Application.csproj" />
|
||||
<ProjectReference Include="..\IRaCIS.Core.Infra.EFCore\IRaCIS.Core.Infra.EFCore.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Properties\PublishProfiles\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Update="NLog.config">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ProjectExtensions><VisualStudio><UserProperties anonymizetagsetting_1json__JsonSchema="http://json.schemastore.org/jovo-language-model" /></VisualStudio></ProjectExtensions>
|
||||
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,345 @@
|
|||
<?xml version="1.0"?>
|
||||
<doc>
|
||||
<assembly>
|
||||
<name>IRaCIS.Core.API</name>
|
||||
</assembly>
|
||||
<members>
|
||||
<member name="M:EasyCaching.Demo.Interceptors.Controllers.ErrorController.Error(System.Int32)">
|
||||
<summary>
|
||||
主要处理 前端404等错误 全局业务异常已统一处理了,非业务错误会来到这里
|
||||
</summary>
|
||||
<param name="code"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:IRaCIS.Api.Controllers.ExtraController">
|
||||
<summary>
|
||||
医生基本信息 、工作信息 专业信息、审核状态
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Api.Controllers.ExtraController.GetDoctorDetail(IRaCIS.Application.Interfaces.IAttachmentService,IRaCIS.Application.Interfaces.IDoctorService,IRaCIS.Application.Interfaces.IEducationService,IRaCIS.Application.Interfaces.ITrialExperienceService,IRaCIS.Application.Interfaces.IResearchPublicationService,IRaCIS.Application.Interfaces.IVacationService,System.Guid)">
|
||||
<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>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Api.Controllers.ExtraController.Login(IRaCIS.Application.Contracts.UserLoginDTO,EasyCaching.Core.IEasyCachingProvider,IRaCIS.Application.Services.IUserService,IRaCIS.Core.Application.Auth.ITokenService,Microsoft.Extensions.Configuration.IConfiguration)">
|
||||
<summary> 系统用户登录接口[New] </summary>
|
||||
</member>
|
||||
<member name="T:IRaCIS.Api.Controllers.FileController">
|
||||
<summary>
|
||||
文件上传
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Api.Controllers.FileController.UploadOrdinaryFile(System.String,System.Guid)">
|
||||
<summary>
|
||||
上传文件[FileUpload]
|
||||
</summary>
|
||||
<param name="attachmentType">附件类型</param>
|
||||
<param name="doctorId">医生Id</param>
|
||||
<returns>返回文件信息</returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Api.Controllers.FileController.UploadNonDoctorFile(System.String)">
|
||||
<summary>
|
||||
上传文件( 不是医生个人的文件)[FileUpload]
|
||||
例如:阅片章程等
|
||||
</summary>
|
||||
<param name="type">文件类型</param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Api.Controllers.FileController.WriteFileAsync(System.IO.Stream,System.String)">
|
||||
<summary>
|
||||
写文件导到磁盘
|
||||
</summary>
|
||||
<param name="stream">流</param>
|
||||
<param name="path">文件保存路径</param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Api.Controllers.FileController.DownloadAttachment(System.Guid[])">
|
||||
<summary>
|
||||
下载多个医生的所有附件
|
||||
</summary>
|
||||
<param name="doctorIds"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Api.Controllers.FileController.DownloadOfficialResume(System.Int32,System.Guid[])">
|
||||
<summary>
|
||||
下载医生官方简历
|
||||
</summary>
|
||||
<param name="language"></param>
|
||||
<param name="doctorIds"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Api.Controllers.FileController.DownloadAttachmentById(System.Guid,System.Guid[])">
|
||||
<summary>
|
||||
下载指定医生的指定附件
|
||||
</summary>
|
||||
<param name="doctorId">医生Id</param>
|
||||
<param name="attachmentIds">要下载的附件Id</param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Api.Controllers.FileController.UploadingStream(System.Guid,System.Guid,System.Guid,System.Guid,System.Guid,IRaCIS.Core.Application.Contracts.Image.IStudyDTFService)">
|
||||
<summary>
|
||||
流式上传 临时文件
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Api.Controllers.FileController.UploadingNoneDicomStream(IRaCIS.Core.Application.Contracts.ArchiveStudyCommand,IRaCIS.Application.Interfaces.IStudyService)">
|
||||
<summary>
|
||||
流式上传 非Dicom文件
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:IRaCIS.Api.Controllers.StudyController">
|
||||
<summary>
|
||||
Study
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Api.Controllers.StudyController.ArchiveStudy(IRaCIS.Core.Application.Contracts.ArchiveStudyCommand)">
|
||||
<summary> 归档</summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.AddOrUpdateTrial(IRaCIS.Application.Contracts.TrialCommand)">
|
||||
<summary> 添加实验项目-返回新增Id[AUTH]</summary>
|
||||
<param name="param"></param>
|
||||
<returns>新记录Id</returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.WorkLoadAddOrUpdate(IRaCIS.Application.Services.IDoctorWorkloadService,IRaCIS.Application.Contracts.WorkloadCommand)">
|
||||
<summary>
|
||||
添加或更新工作量[AUTH]
|
||||
</summary>
|
||||
<param name="_trialWorkloadService"></param>
|
||||
<param name="workLoadAddOrUpdateModel"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.AddOrUpdateExchangeRate(IRaCIS.Application.Interfaces.IExchangeRateService,IRaCIS.Application.Interfaces.IPaymentAdjustmentService,IRaCIS.Application.Contracts.ExchangeRateCommand)">
|
||||
<summary>
|
||||
添加或更新汇率(会触发没有对锁定的费用计算)
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.AddOrUpdateRankPrice(IRaCIS.Application.Interfaces.IReviewerPayInfoService,IRaCIS.Application.Interfaces.IRankPriceService,IRaCIS.Application.Contracts.RankPriceCommand)">
|
||||
<summary>
|
||||
添加或更新 职称单价[AUTH]
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.AddOrUpdateReviewerPayInfo(IRaCIS.Application.Interfaces.IReviewerPayInfoService,IRaCIS.Application.Contracts.ReviewerPayInfoCommand)">
|
||||
<summary>
|
||||
添加或更新(替换)医生支付展信息[AUTH]
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.AddOrUpdateTrialPaymentPrice(IRaCIS.Application.Interfaces.ITrialPaymentPriceService,IRaCIS.Application.Contracts.TrialPaymentPriceCommand)">
|
||||
<summary>
|
||||
保存(替换)项目支付价格信息(会触发没有被锁定的费用计算)[AUTH]
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.AddOrUpdateAwardPriceList(IRaCIS.Application.Interfaces.IVolumeRewardService,System.Collections.Generic.IEnumerable{IRaCIS.Application.Interfaces.AwardPriceCommand})">
|
||||
<summary>
|
||||
批量更新奖励费用[AUTH]
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.CalculateMonthlyPayment(IRaCIS.Application.Contracts.CalculateDoctorAndMonthDTO)">
|
||||
<summary>
|
||||
计算医生月度费用,并将计算的结果存入费用表
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.GetMonthlyPaymentList(IRaCIS.Application.Interfaces.IPaymentService,IRaCIS.Application.Interfaces.IExchangeRateService,IRaCIS.Application.Contracts.MonthlyPaymentQueryDTO)">
|
||||
<summary>
|
||||
Financials /Monthly Payment 列表查询接口
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.InspectionController.VerifySignatureAsync(IRaCIS.Core.Application.Contracts.SignDTO)">
|
||||
<summary> 验证用户签名信息 </summary> ///
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.InspectionController.AddSignRecordAsync(IRaCIS.Core.Application.Contracts.SignDTO)">
|
||||
<summary> 添加签名记录 </summary> ///
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.InspectionController.AddInspectionRecordAsync(IRaCIS.Core.Application.Contracts.DataInspectionAddDTO,System.Nullable{System.Guid})">
|
||||
<summary> 添加稽查记录( 有的会签名,有的不会签名) </summary> ///
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.UploadController.UploadVisitData(System.Guid,IRaCIS.Core.API.Controllers.UploadController.UploadFileTypeEnum,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.SubjectVisit})">
|
||||
<summary>
|
||||
上传临床数据
|
||||
</summary>
|
||||
<param name="subjectVisitId"></param>
|
||||
<param name="uploadType"> 1:DICOM DTF 2:非DIOM DTF 3: 受试者临床数据</param>
|
||||
<param name="_subjectVisitRepository"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.Controllers.UploadController.WriteFileAsync(System.IO.Stream,System.String)">
|
||||
<summary>
|
||||
写文件导到磁盘
|
||||
</summary>
|
||||
<param name="stream">流</param>
|
||||
<param name="path">文件保存路径</param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:IRaCIS.Core.API.IpPolicyRateLimitSetup">
|
||||
<summary>
|
||||
IPLimit限流 启动服务
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.API.NullToEmptyStringResolver.CreateProperties(System.Type,Newtonsoft.Json.MemberSerialization)">
|
||||
<summary>
|
||||
创建属性
|
||||
</summary>
|
||||
<param name="type">类型</param>
|
||||
<param name="memberSerialization">序列化成员</param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.WX.CoreApi.Auth.AuthMiddleware.Invoke(Microsoft.AspNetCore.Http.HttpContext)">
|
||||
<summary>
|
||||
为了前端 一段时间无操作,需要重新登陆
|
||||
</summary>
|
||||
<param name="httpContext"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt.CustomHSJWTService">
|
||||
<summary>
|
||||
对称可逆加密
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt.CustomHSJWTService.GetToken(System.String,System.String)">
|
||||
<summary>
|
||||
用户登录成功以后,用来生成Token的方法
|
||||
</summary>
|
||||
<param name="UserName"></param>
|
||||
<param name="password"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt.CustomRSSJWTervice">
|
||||
<summary>
|
||||
非对称可逆加密
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt.RSAHelper.TryGetKeyParameters(System.String,System.Boolean,System.Security.Cryptography.RSAParameters@)">
|
||||
<summary>
|
||||
从本地文件中读取用来签发 Token 的 RSA Key
|
||||
</summary>
|
||||
<param name="filePath">存放密钥的文件夹路径</param>
|
||||
<param name="withPrivate"></param>
|
||||
<param name="keyParameters"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt.RSAHelper.GenerateAndSaveKey(System.String,System.Boolean)">
|
||||
<summary>
|
||||
生成并保存 RSA 公钥与私钥
|
||||
</summary>
|
||||
<param name="filePath"></param>
|
||||
<param name="withPrivate"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:gRPC.ZHiZHUN.AuthServer.protos.GrpcTokenReflection">
|
||||
<summary>Holder for reflection information generated from Protos/GrpcToken.proto</summary>
|
||||
</member>
|
||||
<member name="P:gRPC.ZHiZHUN.AuthServer.protos.GrpcTokenReflection.Descriptor">
|
||||
<summary>File descriptor for Protos/GrpcToken.proto</summary>
|
||||
</member>
|
||||
<member name="T:gRPC.ZHiZHUN.AuthServer.protos.GetTokenReuqest">
|
||||
<summary>
|
||||
新增用户时需要传递数据消息, 可理解为一个类
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:gRPC.ZHiZHUN.AuthServer.protos.GetTokenReuqest.IdFieldNumber">
|
||||
<summary>Field number for the "id" field.</summary>
|
||||
</member>
|
||||
<member name="F:gRPC.ZHiZHUN.AuthServer.protos.GetTokenReuqest.UserNameFieldNumber">
|
||||
<summary>Field number for the "userName" field.</summary>
|
||||
</member>
|
||||
<member name="F:gRPC.ZHiZHUN.AuthServer.protos.GetTokenReuqest.RealNameFieldNumber">
|
||||
<summary>Field number for the "realName" field.</summary>
|
||||
</member>
|
||||
<member name="F:gRPC.ZHiZHUN.AuthServer.protos.GetTokenReuqest.ReviewerCodeFieldNumber">
|
||||
<summary>Field number for the "reviewerCode" field.</summary>
|
||||
</member>
|
||||
<member name="F:gRPC.ZHiZHUN.AuthServer.protos.GetTokenReuqest.UserTypeEnumIntFieldNumber">
|
||||
<summary>Field number for the "userTypeEnumInt" field.</summary>
|
||||
</member>
|
||||
<member name="F:gRPC.ZHiZHUN.AuthServer.protos.GetTokenReuqest.UserTypeShortNameFieldNumber">
|
||||
<summary>Field number for the "userTypeShortName" field.</summary>
|
||||
</member>
|
||||
<member name="F:gRPC.ZHiZHUN.AuthServer.protos.GetTokenReuqest.IsAdminFieldNumber">
|
||||
<summary>Field number for the "isAdmin" field.</summary>
|
||||
</member>
|
||||
<member name="T:gRPC.ZHiZHUN.AuthServer.protos.GetTokenResponse">
|
||||
<summary>
|
||||
新增时返回的消息格式
|
||||
</summary>
|
||||
</member>
|
||||
<member name="F:gRPC.ZHiZHUN.AuthServer.protos.GetTokenResponse.CodeFieldNumber">
|
||||
<summary>Field number for the "code" field.</summary>
|
||||
</member>
|
||||
<member name="F:gRPC.ZHiZHUN.AuthServer.protos.GetTokenResponse.TokenFieldNumber">
|
||||
<summary>Field number for the "token" field.</summary>
|
||||
</member>
|
||||
<member name="T:gRPC.ZHiZHUN.AuthServer.protos.TokenGrpcService">
|
||||
<summary>
|
||||
service 用标识定义服务的,里面写对应的方法
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:gRPC.ZHiZHUN.AuthServer.protos.TokenGrpcService.Descriptor">
|
||||
<summary>Service descriptor</summary>
|
||||
</member>
|
||||
<member name="T:gRPC.ZHiZHUN.AuthServer.protos.TokenGrpcService.TokenGrpcServiceClient">
|
||||
<summary>Client for TokenGrpcService</summary>
|
||||
</member>
|
||||
<member name="M:gRPC.ZHiZHUN.AuthServer.protos.TokenGrpcService.TokenGrpcServiceClient.#ctor(Grpc.Core.ChannelBase)">
|
||||
<summary>Creates a new client for TokenGrpcService</summary>
|
||||
<param name="channel">The channel to use to make remote calls.</param>
|
||||
</member>
|
||||
<member name="M:gRPC.ZHiZHUN.AuthServer.protos.TokenGrpcService.TokenGrpcServiceClient.#ctor(Grpc.Core.CallInvoker)">
|
||||
<summary>Creates a new client for TokenGrpcService that uses a custom <c>CallInvoker</c>.</summary>
|
||||
<param name="callInvoker">The callInvoker to use to make remote calls.</param>
|
||||
</member>
|
||||
<member name="M:gRPC.ZHiZHUN.AuthServer.protos.TokenGrpcService.TokenGrpcServiceClient.#ctor">
|
||||
<summary>Protected parameterless constructor to allow creation of test doubles.</summary>
|
||||
</member>
|
||||
<member name="M:gRPC.ZHiZHUN.AuthServer.protos.TokenGrpcService.TokenGrpcServiceClient.#ctor(Grpc.Core.ClientBase.ClientBaseConfiguration)">
|
||||
<summary>Protected constructor to allow creation of configured clients.</summary>
|
||||
<param name="configuration">The client configuration.</param>
|
||||
</member>
|
||||
<member name="M:gRPC.ZHiZHUN.AuthServer.protos.TokenGrpcService.TokenGrpcServiceClient.GetUserToken(gRPC.ZHiZHUN.AuthServer.protos.GetTokenReuqest,Grpc.Core.Metadata,System.Nullable{System.DateTime},System.Threading.CancellationToken)">
|
||||
<summary>
|
||||
获取token
|
||||
</summary>
|
||||
<param name="request">The request to send to the server.</param>
|
||||
<param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
|
||||
<param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
|
||||
<param name="cancellationToken">An optional token for canceling the call.</param>
|
||||
<returns>The response received from the server.</returns>
|
||||
</member>
|
||||
<member name="M:gRPC.ZHiZHUN.AuthServer.protos.TokenGrpcService.TokenGrpcServiceClient.GetUserToken(gRPC.ZHiZHUN.AuthServer.protos.GetTokenReuqest,Grpc.Core.CallOptions)">
|
||||
<summary>
|
||||
获取token
|
||||
</summary>
|
||||
<param name="request">The request to send to the server.</param>
|
||||
<param name="options">The options for the call.</param>
|
||||
<returns>The response received from the server.</returns>
|
||||
</member>
|
||||
<member name="M:gRPC.ZHiZHUN.AuthServer.protos.TokenGrpcService.TokenGrpcServiceClient.GetUserTokenAsync(gRPC.ZHiZHUN.AuthServer.protos.GetTokenReuqest,Grpc.Core.Metadata,System.Nullable{System.DateTime},System.Threading.CancellationToken)">
|
||||
<summary>
|
||||
获取token
|
||||
</summary>
|
||||
<param name="request">The request to send to the server.</param>
|
||||
<param name="headers">The initial metadata to send with the call. This parameter is optional.</param>
|
||||
<param name="deadline">An optional deadline for the call. The call will be cancelled if deadline is hit.</param>
|
||||
<param name="cancellationToken">An optional token for canceling the call.</param>
|
||||
<returns>The call object.</returns>
|
||||
</member>
|
||||
<member name="M:gRPC.ZHiZHUN.AuthServer.protos.TokenGrpcService.TokenGrpcServiceClient.GetUserTokenAsync(gRPC.ZHiZHUN.AuthServer.protos.GetTokenReuqest,Grpc.Core.CallOptions)">
|
||||
<summary>
|
||||
获取token
|
||||
</summary>
|
||||
<param name="request">The request to send to the server.</param>
|
||||
<param name="options">The options for the call.</param>
|
||||
<returns>The call object.</returns>
|
||||
</member>
|
||||
<member name="M:gRPC.ZHiZHUN.AuthServer.protos.TokenGrpcService.TokenGrpcServiceClient.NewInstance(Grpc.Core.ClientBase.ClientBaseConfiguration)">
|
||||
<summary>Creates a new instance of client from given <c>ClientBaseConfiguration</c>.</summary>
|
||||
</member>
|
||||
</members>
|
||||
</doc>
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
autoReload="true"
|
||||
throwExceptions="false"
|
||||
internalLogLevel="Off" internalLogFile="c:\temp\nlog-internal.log">
|
||||
<variable name="myvar" value="myvalue"/>
|
||||
|
||||
<targets>
|
||||
|
||||
<target xsi:type="file" name="File" fileName="${basedir}/logs/${shortdate}/${level}.log"
|
||||
layout="${longdate}||${level}||${logger}||${message}||${exception:format=ToString:innerFormat=ToString:maxInnerExceptionLevel=10:separator=\r\n}||end" />
|
||||
</targets>
|
||||
|
||||
<rules>
|
||||
<logger name="*" minlevel="Debug" writeTo="file" />
|
||||
</rules>
|
||||
</nlog>
|
|
@ -0,0 +1,142 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using Autofac.Extensions.DependencyInjection;
|
||||
using Dicom.Imaging;
|
||||
using EasyCaching.Core;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Serilog;
|
||||
using System.Collections.Generic;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using MediatR;
|
||||
using IRaCIS.Core.Application.MediatR.Handlers;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public readonly string environment;
|
||||
public static async Task Main(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
var index = Array.IndexOf(args, "--env");
|
||||
var environment = index > -1
|
||||
? args[index + 1]
|
||||
: "Development";
|
||||
|
||||
//Dicom 浏览
|
||||
//ImageManager.SetImplementation(WinFormsImageManager.Instance);
|
||||
|
||||
var host = CreateHostBuilder(args)
|
||||
.UseEnvironment(environment) //命令行传入环境
|
||||
.ConfigureAppConfiguration((hostContext, config) =>
|
||||
{
|
||||
|
||||
//Console.WriteLine(hostContext.HostingEnvironment.EnvironmentName);
|
||||
config.AddJsonFile("appsettings.json", false, true)
|
||||
.AddJsonFile($"appsettings.{environment}.json", false, true);
|
||||
})
|
||||
.Build();
|
||||
|
||||
//// Serilog
|
||||
SerilogExtension.AddSerilogSetup(environment, host.Services);
|
||||
|
||||
//缓存项目的状态 匿名化数据
|
||||
await InitCache(host);
|
||||
|
||||
//Log.Logger.Error("缓存项目状态完毕");
|
||||
|
||||
host.Run();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
Log.Logger.Error(e.InnerException is null ? e.Message + e.StackTrace : e.InnerException?.Message + e.InnerException?.StackTrace);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region Nlog 废弃
|
||||
//var logger = NLog.Web.NLogBuilder.ConfigureNLog("NLog.config").GetCurrentClassLogger();
|
||||
//try
|
||||
//{
|
||||
// var ihostBuilder = CreateHostBuilder(args);
|
||||
|
||||
// ihostBuilder.UseEnvironment(environment);
|
||||
|
||||
// ihostBuilder.ConfigureAppConfiguration((hostContext, config) =>
|
||||
// {
|
||||
|
||||
// //Console.WriteLine(hostContext.HostingEnvironment.EnvironmentName);
|
||||
// config.AddJsonFile("appsettings.json", false, true)
|
||||
// .AddJsonFile($"appsettings.{environment}.json", false, true);
|
||||
// });
|
||||
|
||||
// var host = ihostBuilder.Build();
|
||||
|
||||
// CacheTrialStatus(host);
|
||||
|
||||
// host.Run();
|
||||
|
||||
//}
|
||||
//catch (Exception exception)
|
||||
//{
|
||||
// //NLog: catch setup errors
|
||||
// logger.Error(exception, "Stopped program because of exception");
|
||||
//}
|
||||
//finally
|
||||
//{
|
||||
// // Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
|
||||
// NLog.LogManager.Shutdown();
|
||||
//}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||
Host.CreateDefaultBuilder(args)
|
||||
.UseWindowsService()
|
||||
.ConfigureWebHostDefaults(webBuilder =>
|
||||
{
|
||||
webBuilder.ConfigureKestrel((context, options) =>
|
||||
{
|
||||
//设置应用服务器Kestrel请求体最大为1GB // if don't set default value is: 30 MB
|
||||
options.Limits.MaxRequestBodySize = long.MaxValue;
|
||||
options.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(30);
|
||||
options.Limits.RequestHeadersTimeout = TimeSpan.FromMinutes(20);
|
||||
|
||||
});
|
||||
webBuilder.UseSerilog();//在宿主机启动的时候配置serilog,与微软ILogger进行整合
|
||||
webBuilder.UseStartup<Startup>();
|
||||
})
|
||||
.UseServiceProviderFactory(new AutofacServiceProviderFactory());//使用Autofac替代本身容器
|
||||
//ConfigureLogging(logging =>
|
||||
//{
|
||||
// logging.ClearProviders();
|
||||
// logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
|
||||
//})
|
||||
//.UseNLog()
|
||||
// NLog: Setup NLog for Dependency injection;
|
||||
|
||||
|
||||
|
||||
|
||||
private static async Task InitCache(IHost host)
|
||||
{
|
||||
var _mediator = host.Services.GetService(typeof(IMediator)) as IMediator;
|
||||
|
||||
await _mediator.Send(new AnonymizeCacheRequest());
|
||||
|
||||
await _mediator.Send(new TrialStateCacheRequest());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:57302",
|
||||
"sslPort": 0
|
||||
}
|
||||
},
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"profiles": {
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"IRaCIS.Core.API": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"applicationUrl": "http://localhost:6100"
|
||||
},
|
||||
"Docker": {
|
||||
"commandName": "Docker",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}",
|
||||
"publishAllPorts": true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
// 使用的是proto3版本
|
||||
syntax = "proto3";
|
||||
// 定义命名空间,后续生成代码时就会生成对应的命名空间
|
||||
option csharp_namespace = "gRPC.ZHiZHUN.AuthServer.protos";
|
||||
|
||||
/*
|
||||
每一句需要用分号结尾
|
||||
message 用来定义请求和返回数据格式
|
||||
tag message后面的值数字代表是字段的标识(tag),不是赋值,
|
||||
*/
|
||||
|
||||
|
||||
// 新增用户时需要传递数据消息, 可理解为一个类
|
||||
message GetTokenReuqest{
|
||||
string id=1;
|
||||
string userName=2;
|
||||
string realName=3;
|
||||
string reviewerCode=4;
|
||||
int32 userTypeEnumInt=5;
|
||||
string userTypeShortName=6;
|
||||
bool isAdmin=7;
|
||||
|
||||
}
|
||||
|
||||
// 新增时返回的消息格式
|
||||
message GetTokenResponse {
|
||||
int32 code=1;
|
||||
string token =2;
|
||||
}
|
||||
|
||||
|
||||
// service 用标识定义服务的,里面写对应的方法
|
||||
service TokenGrpcService{
|
||||
// 获取token
|
||||
rpc GetUserToken(GetTokenReuqest) returns (GetTokenResponse);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
// 新增用户时需要传递数据消息, 可理解为一个类
|
||||
message AddUserReuqest{
|
||||
string name=1;
|
||||
int32 age=2;
|
||||
bool isBoy=3;
|
||||
}
|
||||
// 新增时返回的消息格式
|
||||
message ResultResponse {
|
||||
int32 code=1;
|
||||
string msg =2;
|
||||
}
|
||||
//传递的查询条件信息格式,可理解为平时传入的查询条件对象
|
||||
message QueryUserReuqest{
|
||||
string name=1;
|
||||
}
|
||||
//查询返回的用户信息格式,可理解为返回的类
|
||||
message UserInfoResponse {
|
||||
string name=1;
|
||||
int32 age=2;
|
||||
string gender=3;
|
||||
}
|
||||
|
||||
// service 用标识定义服务的,里面写对应的方法
|
||||
service UserService{
|
||||
// 新增用户
|
||||
rpc AddUser(AddUserReuqest) returns (ResultResponse);
|
||||
// 查询用户
|
||||
rpc GetAllUser(QueryUserReuqest) returns (UserInfoResponse);
|
||||
}
|
||||
*/
|
|
@ -0,0 +1,194 @@
|
|||
using Autofac;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using System;
|
||||
using IRaCIS.Core.Application.Filter;
|
||||
using LogDashboard;
|
||||
using MediatR;
|
||||
using IRaCIS.Core.Application.MediatR.Handlers;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using AspNetCoreRateLimit;
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using System.Globalization;
|
||||
using Microsoft.AspNetCore.Localization;
|
||||
using Localization;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public Startup(IConfiguration configuration)
|
||||
{
|
||||
_configuration = configuration;
|
||||
}
|
||||
public ILogger<Startup> _logger { get; }
|
||||
|
||||
public IConfiguration _configuration { get; }
|
||||
|
||||
//// ConfigureContainer is where you can register things directly
|
||||
//// with Autofac. This runs after ConfigureServices so the things
|
||||
//// here will override registrations made in ConfigureServices.
|
||||
//// Don't build the container; that gets done for you by the factory.
|
||||
// for castle
|
||||
public void ConfigureContainer(ContainerBuilder containerBuilder)
|
||||
{
|
||||
containerBuilder.RegisterModule<AutofacModuleSetup>();
|
||||
|
||||
#region Test
|
||||
//containerBuilder.RegisterType<ClinicalDataService>().PropertiesAutowired().InstancePerLifetimeScope();//注册仓储
|
||||
|
||||
//var container = containerBuilder.Build();
|
||||
|
||||
//// Now you can resolve services using Autofac. For example,
|
||||
//// this line will execute the lambda expression registered
|
||||
//// to the IConfigReader service.
|
||||
//using (var scope = container.BeginLifetimeScope())
|
||||
//{
|
||||
// var reader = scope.Resolve<BaseService>();
|
||||
|
||||
// var test = scope.Resolve<ClinicalDataService>();
|
||||
// var test2 = scope.Resolve<IClinicalDataService>();
|
||||
|
||||
// var test3 = scope.Resolve<IEFUnitOfWork<IRaCISDBContext>>();
|
||||
//}
|
||||
#endregion
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
//本地化
|
||||
services.AddJsonLocalization(options => options.ResourcesPath = "Resources");
|
||||
|
||||
// 异常、参数统一验证过滤器、Json序列化配置、字符串参数绑型统一Trim()
|
||||
services.AddControllers(options =>
|
||||
{
|
||||
//options.Filters.Add<LogActionFilter>();
|
||||
options.Filters.Add<ModelActionFilter>();
|
||||
options.Filters.Add<ProjectExceptionFilter>();
|
||||
options.Filters.Add<UnitOfWorkFilter>();
|
||||
})
|
||||
.AddDataAnnotationsLocalization(options => {
|
||||
options.DataAnnotationLocalizerProvider = (type, factory) =>
|
||||
factory.Create(typeof(SharedResource));
|
||||
})
|
||||
.AddNewtonsoftJsonSetup(); // NewtonsoftJson 序列化 处理
|
||||
|
||||
//动态WebApi + UnifiedApiResultFilter 省掉控制器代码
|
||||
services.AddDynamicWebApiSetup();
|
||||
//AutoMapper
|
||||
services.AddAutoMapperSetup();
|
||||
//EF ORM QueryWithNoLock
|
||||
services.AddEFSetup(_configuration);
|
||||
//Http 响应压缩
|
||||
services.AddResponseCompressionSetup();
|
||||
//Swagger Api 文档
|
||||
services.AddSwaggerSetup();
|
||||
//JWT Token 验证
|
||||
services.AddJWTAuthSetup(_configuration);
|
||||
// MediatR 进程内消息 事件解耦 从程序集中 注册命令和handler对应关系
|
||||
services.AddMediatR(typeof(ConsistencyVerificationHandler).Assembly);
|
||||
// EasyCaching 缓存
|
||||
services.AddEasyCachingSetup();
|
||||
// hangfire 定时任务框架 有界面,更友好~
|
||||
services.AddhangfireSetup(_configuration);
|
||||
// QuartZ 定时任务框架 使用了hangfire 暂时不用,后续需要可以打开,已经配好
|
||||
services.AddQuartZSetup(_configuration);
|
||||
// 保护上传文件
|
||||
services.AddStaticFileAuthorizationSetup();
|
||||
////HttpReports 暂时废弃
|
||||
//services.AddHttpReports().AddHttpTransport();
|
||||
//Serilog 日志可视化 LogDashboard日志
|
||||
services.AddLogDashboardSetup();
|
||||
//上传限制 配置
|
||||
services.Configure<FormOptions>(options =>
|
||||
{
|
||||
options.MultipartBodyLengthLimit = int.MaxValue;
|
||||
options.ValueCountLimit = int.MaxValue;
|
||||
options.ValueLengthLimit = int.MaxValue;
|
||||
});
|
||||
//IP 限流 可设置白名单 或者黑名单
|
||||
//services.AddIpPolicyRateLimitSetup(_configuration);
|
||||
// 用户类型 策略授权
|
||||
services.AddAuthorizationPolicySetup(_configuration);
|
||||
//转发头设置 获取真实IP
|
||||
services.Configure<ForwardedHeadersOptions>(options =>
|
||||
{
|
||||
options.ForwardedHeaders =
|
||||
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
|
||||
});
|
||||
//Dicom影像渲染图片 跨平台
|
||||
services.AddDicomSetup();
|
||||
|
||||
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
{
|
||||
//本地化
|
||||
app.UseLocalization();
|
||||
|
||||
app.UseForwardedHeaders();
|
||||
|
||||
//不需要 token 访问的静态文件 wwwroot css, JavaScript, and images don't require authentication.
|
||||
app.UseStaticFiles();
|
||||
|
||||
//LogDashboard
|
||||
app.UseLogDashboard("/LogDashboard");
|
||||
|
||||
//hangfire
|
||||
app.UseHangfireConfig(env);
|
||||
|
||||
// //暂时废弃
|
||||
//app.UseHttpReports();
|
||||
|
||||
//限流 中间件
|
||||
//app.UseIpRateLimiting();
|
||||
|
||||
//响应压缩
|
||||
app.UseResponseCompression();
|
||||
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
else
|
||||
{
|
||||
//app.UseHsts();
|
||||
}
|
||||
Console.WriteLine("当前环境: " + env.EnvironmentName);
|
||||
|
||||
//app.UseMiddleware<AuthMiddleware>();
|
||||
|
||||
// 特殊异常处理 比如 404
|
||||
app.UseStatusCodePagesWithReExecute("/Error/{0}");
|
||||
|
||||
SwaggerSetup.Configure(app, env);
|
||||
|
||||
|
||||
|
||||
//serilog 记录请求的用户信息
|
||||
app.UseSerilogConfig(env);
|
||||
|
||||
app.UseRouting();
|
||||
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
//文件伺服 必须带Token 访问
|
||||
app.UseIRacisHostStaticFileStore(env);
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapControllers();
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace EFSaving.Concurrency
|
||||
{
|
||||
public class Sample
|
||||
{
|
||||
public static void Run()
|
||||
{
|
||||
// Ensure database is created and has a person in it
|
||||
using (var setupContext = new PersonContext())
|
||||
{
|
||||
setupContext.Database.EnsureDeleted();
|
||||
setupContext.Database.EnsureCreated();
|
||||
|
||||
setupContext.People.Add(new Person { FirstName = "John", LastName = "Doe" });
|
||||
setupContext.SaveChanges();
|
||||
}
|
||||
|
||||
#region ConcurrencyHandlingCode
|
||||
using var context = new PersonContext();
|
||||
// Fetch a person from database and change phone number
|
||||
var person = context.People.Single(p => p.PersonId == 1);
|
||||
person.PhoneNumber = "555-555-5555";
|
||||
|
||||
// Change the person's name in the database to simulate a concurrency conflict
|
||||
context.Database.ExecuteSqlRaw(
|
||||
"UPDATE dbo.People SET FirstName = 'Jane' WHERE PersonId = 1");
|
||||
|
||||
var saved = false;
|
||||
while (!saved)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Attempt to save changes to the database
|
||||
context.SaveChanges();
|
||||
saved = true;
|
||||
}
|
||||
catch (DbUpdateConcurrencyException ex)
|
||||
{
|
||||
foreach (var entry in ex.Entries)
|
||||
{
|
||||
if (entry.Entity is Person)
|
||||
{
|
||||
var proposedValues = entry.CurrentValues;
|
||||
var databaseValues = entry.GetDatabaseValues();
|
||||
|
||||
foreach (var property in proposedValues.Properties)
|
||||
{
|
||||
var proposedValue = proposedValues[property];
|
||||
var databaseValue = databaseValues[property];
|
||||
|
||||
// TODO: decide which value should be written to database
|
||||
// proposedValues[property] = <value to be saved>;
|
||||
}
|
||||
|
||||
// Refresh original values to bypass next concurrency check
|
||||
entry.OriginalValues.SetValues(databaseValues);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException(
|
||||
"Don't know how to handle concurrency conflicts for "
|
||||
+ entry.Metadata.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class PersonContext : DbContext
|
||||
{
|
||||
public DbSet<Person> People { get; set; }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
// Requires NuGet package Microsoft.EntityFrameworkCore.SqlServer
|
||||
optionsBuilder.UseSqlServer(
|
||||
@"Server=(localdb)\mssqllocaldb;Database=EFSaving.Concurrency;Trusted_Connection=True");
|
||||
}
|
||||
}
|
||||
|
||||
public class Person
|
||||
{
|
||||
public int PersonId { get; set; }
|
||||
|
||||
[ConcurrencyCheck]
|
||||
public string FirstName { get; set; }
|
||||
|
||||
[ConcurrencyCheck]
|
||||
public string LastName { get; set; }
|
||||
|
||||
public string PhoneNumber { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using System;
|
||||
|
||||
namespace IRaCIS.Core.API.Filter
|
||||
{
|
||||
public class EnableBufferingAttribute : Attribute, IResourceFilter
|
||||
{
|
||||
public void OnResourceExecuting(ResourceExecutingContext context)
|
||||
{
|
||||
context.HttpContext.Request.EnableBuffering();
|
||||
}
|
||||
|
||||
public void OnResourceExecuted(ResourceExecutedContext context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,270 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
||||
namespace IRaCIS.Core.API.Utility
|
||||
{
|
||||
public static class FileHelpers
|
||||
{
|
||||
private static readonly byte[] _allowedChars = { };
|
||||
// For more file signatures, see the File Signatures Database (https://www.filesignatures.net/)
|
||||
// and the official specifications for the file types you wish to add.
|
||||
private static readonly Dictionary<string, List<byte[]>> _fileSignature = new Dictionary<string, List<byte[]>>
|
||||
{
|
||||
{ ".gif", new List<byte[]> { new byte[] { 0x47, 0x49, 0x46, 0x38 } } },
|
||||
{ ".png", new List<byte[]> { new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A } } },
|
||||
{ ".jpeg", new List<byte[]>
|
||||
{
|
||||
new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 },
|
||||
new byte[] { 0xFF, 0xD8, 0xFF, 0xE2 },
|
||||
new byte[] { 0xFF, 0xD8, 0xFF, 0xE3 },
|
||||
}
|
||||
},
|
||||
{ ".jpg", new List<byte[]>
|
||||
{
|
||||
new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 },
|
||||
new byte[] { 0xFF, 0xD8, 0xFF, 0xE1 },
|
||||
new byte[] { 0xFF, 0xD8, 0xFF, 0xE8 },
|
||||
}
|
||||
},
|
||||
{ ".zip", new List<byte[]>
|
||||
{
|
||||
new byte[] { 0x50, 0x4B, 0x03, 0x04 },
|
||||
new byte[] { 0x50, 0x4B, 0x4C, 0x49, 0x54, 0x45 },
|
||||
new byte[] { 0x50, 0x4B, 0x53, 0x70, 0x58 },
|
||||
new byte[] { 0x50, 0x4B, 0x05, 0x06 },
|
||||
new byte[] { 0x50, 0x4B, 0x07, 0x08 },
|
||||
new byte[] { 0x57, 0x69, 0x6E, 0x5A, 0x69, 0x70 },
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// **WARNING!**
|
||||
// In the following file processing methods, the file's content isn't scanned.
|
||||
// In most production scenarios, an anti-virus/anti-malware scanner API is
|
||||
// used on the file before making the file available to users or other
|
||||
// systems. For more information, see the topic that accompanies this sample
|
||||
// app.
|
||||
|
||||
public static async Task<byte[]> ProcessFormFile<T>(IFormFile formFile,
|
||||
ModelStateDictionary modelState, string[] permittedExtensions,
|
||||
long sizeLimit)
|
||||
{
|
||||
var fieldDisplayName = string.Empty;
|
||||
|
||||
// Use reflection to obtain the display name for the model
|
||||
// property associated with this IFormFile. If a display
|
||||
// name isn't found, error messages simply won't show
|
||||
// a display name.
|
||||
MemberInfo property =
|
||||
typeof(T).GetProperty(
|
||||
formFile.Name.Substring(formFile.Name.IndexOf(".",
|
||||
StringComparison.Ordinal) + 1));
|
||||
|
||||
if (property != null)
|
||||
{
|
||||
if (property.GetCustomAttribute(typeof(DisplayAttribute)) is
|
||||
DisplayAttribute displayAttribute)
|
||||
{
|
||||
fieldDisplayName = $"{displayAttribute.Name} ";
|
||||
}
|
||||
}
|
||||
|
||||
// Don't trust the file name sent by the client. To display
|
||||
// the file name, HTML-encode the value.
|
||||
var trustedFileNameForDisplay = WebUtility.HtmlEncode(
|
||||
formFile.FileName);
|
||||
|
||||
// Check the file length. This check doesn't catch files that only have
|
||||
// a BOM as their content.
|
||||
if (formFile.Length == 0)
|
||||
{
|
||||
modelState.AddModelError(formFile.Name,
|
||||
$"{fieldDisplayName}({trustedFileNameForDisplay}) is empty.");
|
||||
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
if (formFile.Length > sizeLimit)
|
||||
{
|
||||
var megabyteSizeLimit = sizeLimit / 1048576;
|
||||
modelState.AddModelError(formFile.Name,
|
||||
$"{fieldDisplayName}({trustedFileNameForDisplay}) exceeds " +
|
||||
$"{megabyteSizeLimit:N1} MB.");
|
||||
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
await formFile.CopyToAsync(memoryStream);
|
||||
|
||||
// Check the content length in case the file's only
|
||||
// content was a BOM and the content is actually
|
||||
// empty after removing the BOM.
|
||||
if (memoryStream.Length == 0)
|
||||
{
|
||||
modelState.AddModelError(formFile.Name,
|
||||
$"{fieldDisplayName}({trustedFileNameForDisplay}) is empty.");
|
||||
}
|
||||
|
||||
if (!IsValidFileExtensionAndSignature(
|
||||
formFile.FileName, memoryStream, permittedExtensions))
|
||||
{
|
||||
modelState.AddModelError(formFile.Name,
|
||||
$"{fieldDisplayName}({trustedFileNameForDisplay}) file " +
|
||||
"type isn't permitted or the file's signature " +
|
||||
"doesn't match the file's extension.");
|
||||
}
|
||||
else
|
||||
{
|
||||
return memoryStream.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
modelState.AddModelError(formFile.Name,
|
||||
$"{fieldDisplayName}({trustedFileNameForDisplay}) upload failed. " +
|
||||
$"Please contact the Help Desk for support. Error: {ex.HResult}");
|
||||
}
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
public static async Task<byte[]> ProcessStreamedFile(
|
||||
MultipartSection section, ContentDispositionHeaderValue contentDisposition,
|
||||
ModelStateDictionary modelState, string[] permittedExtensions, long sizeLimit)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
await section.Body.CopyToAsync(memoryStream);
|
||||
|
||||
// Check if the file is empty or exceeds the size limit.
|
||||
if (memoryStream.Length == 0)
|
||||
{
|
||||
modelState.AddModelError("File", "The file is empty.");
|
||||
}
|
||||
else if (memoryStream.Length > sizeLimit)
|
||||
{
|
||||
var megabyteSizeLimit = sizeLimit / 1048576;
|
||||
modelState.AddModelError("File",
|
||||
$"The file exceeds {megabyteSizeLimit:N1} MB.");
|
||||
}
|
||||
else if (!IsValidFileExtensionAndSignature(
|
||||
contentDisposition.FileName.Value, memoryStream,
|
||||
permittedExtensions))
|
||||
{
|
||||
modelState.AddModelError("File",
|
||||
"The file type isn't permitted or the file's " +
|
||||
"signature doesn't match the file's extension.");
|
||||
}
|
||||
else
|
||||
{
|
||||
return memoryStream.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
modelState.AddModelError("File",
|
||||
"The upload failed. Please contact the Help Desk " +
|
||||
$" for support. Error: {ex.HResult}");
|
||||
// Log the exception
|
||||
}
|
||||
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
private static bool IsValidFileExtensionAndSignature(string fileName, Stream data, string[] permittedExtensions)
|
||||
{
|
||||
if (string.IsNullOrEmpty(fileName) || data == null || data.Length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var ext = Path.GetExtension(fileName).ToLowerInvariant();
|
||||
|
||||
if (string.IsNullOrEmpty(ext) || !permittedExtensions.Contains(ext))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
data.Position = 0;
|
||||
|
||||
using (var reader = new BinaryReader(data))
|
||||
{
|
||||
if (ext.Equals(".txt") || ext.Equals(".csv") || ext.Equals(".prn"))
|
||||
{
|
||||
if (_allowedChars.Length == 0)
|
||||
{
|
||||
// Limits characters to ASCII encoding.
|
||||
for (var i = 0; i < data.Length; i++)
|
||||
{
|
||||
if (reader.ReadByte() > sbyte.MaxValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Limits characters to ASCII encoding and
|
||||
// values of the _allowedChars array.
|
||||
for (var i = 0; i < data.Length; i++)
|
||||
{
|
||||
var b = reader.ReadByte();
|
||||
if (b > sbyte.MaxValue ||
|
||||
!_allowedChars.Contains(b))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Uncomment the following code block if you must permit
|
||||
// files whose signature isn't provided in the _fileSignature
|
||||
// dictionary. We recommend that you add file signatures
|
||||
// for files (when possible) for all file types you intend
|
||||
// to allow on the system and perform the file signature
|
||||
// check.
|
||||
|
||||
//if (!_fileSignature.ContainsKey(ext))
|
||||
//{
|
||||
// return true;
|
||||
//}
|
||||
|
||||
|
||||
// File signature check
|
||||
// --------------------
|
||||
// With the file signatures provided in the _fileSignature
|
||||
// dictionary, the following code tests the input content's
|
||||
// file signature.
|
||||
|
||||
//var signatures = _fileSignature[ext];
|
||||
//var headerBytes = reader.ReadBytes(signatures.Max(m => m.Length));
|
||||
|
||||
//return signatures.Any(signature =>
|
||||
// headerBytes.Take(signature.Length).SequenceEqual(signature));
|
||||
|
||||
//test
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt
|
||||
{
|
||||
/// <summary>
|
||||
/// 对称可逆加密
|
||||
/// </summary>
|
||||
public class CustomHSJWTService : ICustomJWTService
|
||||
{
|
||||
#region Option注入
|
||||
private readonly JWTTokenOptions _JWTTokenOptions;
|
||||
public CustomHSJWTService(IOptionsMonitor<JWTTokenOptions> jwtTokenOptions)
|
||||
{
|
||||
this._JWTTokenOptions = jwtTokenOptions.CurrentValue;
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 用户登录成功以后,用来生成Token的方法
|
||||
/// </summary>
|
||||
/// <param name="UserName"></param>
|
||||
/// <param name="password"></param>
|
||||
/// <returns></returns>
|
||||
public string GetToken(string UserName, string password)
|
||||
{
|
||||
#region 有效载荷,大家可以自己写,爱写多少写多少;尽量避免敏感信息
|
||||
var claims = new[]
|
||||
{
|
||||
new Claim(ClaimTypes.Name, UserName),
|
||||
new Claim("NickName",UserName),
|
||||
new Claim("Role","Administrator"),//传递其他信息
|
||||
new Claim("ABCC","ABCC"),
|
||||
new Claim("ABCCDDDDD","ABCCDDDDD"),
|
||||
new Claim("Student","甜酱油")
|
||||
};
|
||||
|
||||
//需要加密:需要加密key:
|
||||
//Nuget引入:Microsoft.IdentityModel.Tokens
|
||||
SymmetricSecurityKey key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_JWTTokenOptions.SecurityKey));
|
||||
|
||||
SigningCredentials creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
|
||||
|
||||
//Nuget引入:System.IdentityModel.Tokens.Jwt
|
||||
JwtSecurityToken token = new JwtSecurityToken(
|
||||
issuer: _JWTTokenOptions.Issuer,
|
||||
audience: _JWTTokenOptions.Audience,
|
||||
claims: claims,
|
||||
expires: DateTime.Now.AddMinutes(5),//5分钟有效期
|
||||
signingCredentials: creds);
|
||||
|
||||
string returnToken = new JwtSecurityTokenHandler().WriteToken(token);
|
||||
return returnToken;
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt
|
||||
{
|
||||
/// <summary>
|
||||
/// 非对称可逆加密
|
||||
/// </summary>
|
||||
public class CustomRSSJWTervice : ICustomJWTService
|
||||
|
||||
{
|
||||
#region Option注入
|
||||
private readonly JWTTokenOptions _JWTTokenOptions;
|
||||
public CustomRSSJWTervice(IOptionsMonitor<JWTTokenOptions> jwtTokenOptions)
|
||||
{
|
||||
this._JWTTokenOptions = jwtTokenOptions.CurrentValue;
|
||||
}
|
||||
#endregion
|
||||
|
||||
public string GetToken(string userName, string password)
|
||||
{
|
||||
#region 使用加密解密Key 非对称
|
||||
string keyDir = Directory.GetCurrentDirectory();
|
||||
if (RSAHelper.TryGetKeyParameters(keyDir, true, out RSAParameters keyParams) == false)
|
||||
{
|
||||
keyParams = RSAHelper.GenerateAndSaveKey(keyDir);
|
||||
}
|
||||
#endregion
|
||||
|
||||
//string jtiCustom = Guid.NewGuid().ToString();//用来标识 Token
|
||||
Claim[] claims = new[]
|
||||
{
|
||||
new Claim(ClaimTypes.Name, userName),
|
||||
new Claim(ClaimTypes.Role,"admin"),
|
||||
new Claim("password",password)
|
||||
};
|
||||
|
||||
SigningCredentials credentials = new SigningCredentials(new RsaSecurityKey(keyParams), SecurityAlgorithms.RsaSha256Signature);
|
||||
|
||||
var token = new JwtSecurityToken(
|
||||
issuer: this._JWTTokenOptions.Issuer,
|
||||
audience: this._JWTTokenOptions.Audience,
|
||||
claims: claims,
|
||||
expires: DateTime.Now.AddMinutes(60),//5分钟有效期
|
||||
signingCredentials: credentials);
|
||||
|
||||
var handler = new JwtSecurityTokenHandler();
|
||||
string tokenString = handler.WriteToken(token);
|
||||
return tokenString;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt
|
||||
{
|
||||
public interface ICustomJWTService
|
||||
{
|
||||
string GetToken(string UserName, string password);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt
|
||||
{
|
||||
public class JWTTokenOptions
|
||||
{
|
||||
public string Audience
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public string SecurityKey
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
//public SigningCredentials Credentials
|
||||
//{
|
||||
// get;
|
||||
// set;
|
||||
//}
|
||||
public string Issuer
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt
|
||||
{
|
||||
public class RSAHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 从本地文件中读取用来签发 Token 的 RSA Key
|
||||
/// </summary>
|
||||
/// <param name="filePath">存放密钥的文件夹路径</param>
|
||||
/// <param name="withPrivate"></param>
|
||||
/// <param name="keyParameters"></param>
|
||||
/// <returns></returns>
|
||||
public static bool TryGetKeyParameters(string filePath, bool withPrivate, out RSAParameters keyParameters)
|
||||
{
|
||||
string filename = withPrivate ? "key.json" : "key.public.json";
|
||||
string fileTotalPath = Path.Combine(filePath, filename);
|
||||
keyParameters = default(RSAParameters);
|
||||
if (!File.Exists(fileTotalPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
keyParameters = JsonConvert.DeserializeObject<RSAParameters>(File.ReadAllText(fileTotalPath));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 生成并保存 RSA 公钥与私钥
|
||||
/// </summary>
|
||||
/// <param name="filePath"></param>
|
||||
/// <param name="withPrivate"></param>
|
||||
/// <returns></returns>
|
||||
public static RSAParameters GenerateAndSaveKey(string filePath, bool withPrivate = true)
|
||||
{
|
||||
RSAParameters publicKeys, privateKeys;
|
||||
using (var rsa = new RSACryptoServiceProvider(2048))//即时生成
|
||||
{
|
||||
try
|
||||
{
|
||||
privateKeys = rsa.ExportParameters(true);
|
||||
publicKeys = rsa.ExportParameters(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
rsa.PersistKeyInCsp = false;
|
||||
}
|
||||
}
|
||||
File.WriteAllText(Path.Combine(filePath, "key.json"), JsonConvert.SerializeObject(privateKeys));
|
||||
File.WriteAllText(Path.Combine(filePath, "key.public.json"), JsonConvert.SerializeObject(publicKeys));
|
||||
return withPrivate ? privateKeys : publicKeys;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
||||
namespace IRaCIS.Core.API.Utility
|
||||
{
|
||||
public static class MultipartRequestHelper
|
||||
{
|
||||
// Content-Type: multipart/form-data; boundary="----WebKitFormBoundarymx2fSWqWSd0OxQqq"
|
||||
// The spec at https://tools.ietf.org/html/rfc2046#section-5.1 states that 70 characters is a reasonable limit.
|
||||
public static string GetBoundary(MediaTypeHeaderValue contentType, int lengthLimit)
|
||||
{
|
||||
var boundary = HeaderUtilities.RemoveQuotes(contentType.Boundary).Value;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(boundary))
|
||||
{
|
||||
throw new InvalidDataException("Missing content-type boundary.");
|
||||
}
|
||||
|
||||
if (boundary.Length > lengthLimit)
|
||||
{
|
||||
throw new InvalidDataException(
|
||||
$"Multipart boundary length limit {lengthLimit} exceeded.");
|
||||
}
|
||||
|
||||
return boundary;
|
||||
}
|
||||
|
||||
public static bool IsMultipartContentType(string contentType)
|
||||
{
|
||||
return !string.IsNullOrEmpty(contentType)
|
||||
&& contentType.IndexOf("multipart/", StringComparison.OrdinalIgnoreCase) >= 0;
|
||||
}
|
||||
|
||||
public static bool HasFormDataContentDisposition(ContentDispositionHeaderValue contentDisposition)
|
||||
{
|
||||
// Content-Disposition: form-data; name="key";
|
||||
return contentDisposition != null
|
||||
&& contentDisposition.DispositionType.Equals("form-data")
|
||||
&& string.IsNullOrEmpty(contentDisposition.FileName.Value)
|
||||
&& string.IsNullOrEmpty(contentDisposition.FileNameStar.Value);
|
||||
}
|
||||
|
||||
public static bool HasFileContentDisposition(ContentDispositionHeaderValue contentDisposition)
|
||||
{
|
||||
// Content-Disposition: form-data; name="myfile1"; filename="Misc 002.jpg"
|
||||
return contentDisposition != null
|
||||
&& contentDisposition.DispositionType.Equals("form-data")
|
||||
&& (!string.IsNullOrEmpty(contentDisposition.FileName.Value)
|
||||
|| !string.IsNullOrEmpty(contentDisposition.FileNameStar.Value));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
using System;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using EasyCaching.Core;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace IRaCIS.WX.CoreApi.Auth
|
||||
{
|
||||
public class AuthMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly IEasyCachingProvider _provider;
|
||||
public AuthMiddleware(RequestDelegate next, IEasyCachingProvider provider)
|
||||
{
|
||||
_next = next;
|
||||
_provider = provider;
|
||||
}
|
||||
/// <summary>
|
||||
///为了前端 一段时间无操作,需要重新登陆
|
||||
/// </summary>
|
||||
/// <param name="httpContext"></param>
|
||||
/// <returns></returns>
|
||||
public async Task Invoke(HttpContext httpContext)
|
||||
{
|
||||
|
||||
var isLogin = httpContext.Request.Path.ToString().ToLower().Contains("login");
|
||||
|
||||
var result = await httpContext.AuthenticateAsync(JwtBearerDefaults.AuthenticationScheme);
|
||||
|
||||
if (!isLogin)
|
||||
{
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
httpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
|
||||
await httpContext.Response.WriteAsync("Unauthorized");
|
||||
}
|
||||
else
|
||||
{
|
||||
var toekn = result.Properties.Items[".Token.access_token"];
|
||||
var jwtHandler = new JwtSecurityTokenHandler();
|
||||
JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(toekn);
|
||||
object userId;
|
||||
jwtToken.Payload.TryGetValue("id", out userId);
|
||||
|
||||
var cacheValueExist = await _provider.ExistsAsync(userId.ToString()); //Get<string>(userId.ToString()).ToString();
|
||||
if (!cacheValueExist)
|
||||
{
|
||||
httpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
|
||||
await httpContext.Response.WriteAsync("Unauthorized");
|
||||
}
|
||||
else
|
||||
{
|
||||
await _provider.SetAsync(userId.ToString(), userId.ToString(), TimeSpan.FromMinutes(AppSettings.LoginExpiredTimeSpan));
|
||||
httpContext.User = result.Principal;
|
||||
await _next.Invoke(httpContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
else await _next.Invoke(httpContext);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
using Hangfire;
|
||||
using Hangfire.Dashboard;
|
||||
using IRaCIS.Application.Services.BackGroundJob;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
|
||||
|
||||
public static class HangfireConfig
|
||||
{
|
||||
|
||||
public static void UseHangfireConfig(this IApplicationBuilder app, IWebHostEnvironment env)
|
||||
{
|
||||
app.UseHangfireDashboard("/api/hangfire", new DashboardOptions()
|
||||
{
|
||||
//直接访问,没有带token 获取不到用户身份信息,所以这种自定义授权暂时没法使用
|
||||
//Authorization = new[] { new hangfireAuthorizationFilter() }
|
||||
|
||||
//本地请求 才能看
|
||||
Authorization = new[] { new LocalRequestsOnlyAuthorizationFilter() }
|
||||
|
||||
});
|
||||
|
||||
#region hangfire
|
||||
//// 延迟任务执行 1秒之后执行 有时启动没运行 换成添加到队列中
|
||||
//BackgroundJob.Schedule<ICacheTrialStatusJob>(t => t.MemoryCacheTrialStatus(), TimeSpan.FromSeconds(1));
|
||||
////添加到后台任务队列,
|
||||
//BackgroundJob.Enqueue<ICacheTrialStatusJob>(t => t.MemoryCacheTrialStatus());
|
||||
|
||||
//周期性任务,1天执行一次
|
||||
RecurringJob.AddOrUpdate<ICacheTrialStatusJob>(t => t.MemoryCacheTrialStatus(), Cron.Daily);
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
using Hangfire.Dashboard;
|
||||
|
||||
namespace IRaCIS.Core.API.Filter
|
||||
{
|
||||
public class hangfireAuthorizationFilter : IDashboardAuthorizationFilter
|
||||
{
|
||||
public bool Authorize(DashboardContext context)
|
||||
{
|
||||
var httpContext = context.GetHttpContext();
|
||||
|
||||
// Allow all authenticated users to see the Dashboard (potentially dangerous).
|
||||
return httpContext.User.Identity.IsAuthenticated;
|
||||
|
||||
//return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
using IRaCIS.Core.Domain.Share;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using System.IO;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
public static class IRacisHostFileStore
|
||||
{
|
||||
|
||||
public static void UseIRacisHostStaticFileStore(this IApplicationBuilder app, IWebHostEnvironment env)
|
||||
{
|
||||
|
||||
var uploadPath = Path.Combine(Directory.GetParent(env.ContentRootPath.TrimEnd('\\')).FullName, StaticData.UploadFileFolder);
|
||||
var dicomPath = Path.Combine(Directory.GetParent(env.ContentRootPath.TrimEnd('\\')).FullName, StaticData.TrialDataFolder);
|
||||
|
||||
|
||||
if (!Directory.Exists(uploadPath))
|
||||
{
|
||||
Directory.CreateDirectory(uploadPath);
|
||||
}
|
||||
|
||||
if (!Directory.Exists(dicomPath))
|
||||
{
|
||||
Directory.CreateDirectory(dicomPath);
|
||||
}
|
||||
|
||||
app.UseStaticFiles(new StaticFileOptions
|
||||
{
|
||||
FileProvider = new PhysicalFileProvider(uploadPath),
|
||||
RequestPath = $"/{StaticData.UploadFileFolder}"
|
||||
});
|
||||
|
||||
|
||||
app.UseStaticFiles(new StaticFileOptions
|
||||
{
|
||||
FileProvider = new PhysicalFileProvider(dicomPath),
|
||||
RequestPath = $"/{StaticData.TrialDataFolder}"
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Localization;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
public static class LocalizationConfig
|
||||
{
|
||||
|
||||
public static void UseLocalization(this IApplicationBuilder app)
|
||||
{
|
||||
var supportedCultures = new List<CultureInfo>
|
||||
{
|
||||
new CultureInfo("en-US"),
|
||||
new CultureInfo("zh-CN")
|
||||
};
|
||||
|
||||
var options = new RequestLocalizationOptions
|
||||
{
|
||||
//DefaultRequestCulture = new RequestCulture("en-US"),
|
||||
SupportedCultures = supportedCultures,
|
||||
SupportedUICultures = supportedCultures,
|
||||
|
||||
ApplyCurrentCultureToResponseHeaders = true
|
||||
};
|
||||
|
||||
//options.RequestCultureProviders.RemoveAt(0);
|
||||
//options.RequestCultureProviders.RemoveAt(1);
|
||||
|
||||
app.UseRequestLocalization(options);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
using LogDashboard;
|
||||
using LogDashboard.Authorization;
|
||||
|
||||
namespace IRaCIS.Core.API.Filter
|
||||
{
|
||||
|
||||
public class LogDashBoardAuthFilter : ILogDashboardAuthorizationFilter
|
||||
{
|
||||
//在此可以利用 本系统的UerTypeEnum 判断
|
||||
public bool Authorization(LogDashboardContext context)
|
||||
{
|
||||
return context.HttpContext.User.Identity.IsAuthenticated;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using Serilog;
|
||||
using Serilog.Context;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IRaCIS.Core.API._PipelineExtensions.Serilog
|
||||
{
|
||||
public class RequestResponseLoggingMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
|
||||
public RequestResponseLoggingMiddleware(RequestDelegate next)
|
||||
{
|
||||
_next = next;
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext context)
|
||||
{
|
||||
var ContentType = context.Request.ContentType;
|
||||
|
||||
var isUploadRelation = false;
|
||||
|
||||
if (ContentType != null)
|
||||
{
|
||||
isUploadRelation = context.Request.ContentType.Contains("multipart/form-data") || context.Request.ContentType.Contains("boundary");
|
||||
}
|
||||
|
||||
|
||||
if (isUploadRelation)
|
||||
{
|
||||
using (LogContext.PushProperty("RequestBody", string.Empty))
|
||||
{
|
||||
await _next.Invoke(context);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var requestBodyPayload = await ReadRequestBody(context.Request);
|
||||
|
||||
using (LogContext.PushProperty("RequestBody", requestBodyPayload))
|
||||
{
|
||||
await _next.Invoke(context);
|
||||
}
|
||||
}
|
||||
|
||||
//var request = context.Request;
|
||||
|
||||
|
||||
// Read and log request body data
|
||||
|
||||
//SerilogHelper.RequestPayload = requestBodyPayload;
|
||||
|
||||
#region ResponseBody
|
||||
|
||||
//// Read and log response body data
|
||||
//// Copy a pointer to the original response body stream
|
||||
//var originalResponseBodyStream = context.Response.Body;
|
||||
|
||||
//// Create a new memory stream...
|
||||
//using (var responseBody = new MemoryStream())
|
||||
//{
|
||||
// // ...and use that for the temporary response body
|
||||
// context.Response.Body = responseBody;
|
||||
|
||||
// // Continue down the Middleware pipeline, eventually returning to this class
|
||||
//await _next(context);
|
||||
|
||||
// // Copy the contents of the new memory stream (which contains the response) to the original stream, which is then returned to the client.
|
||||
// await responseBody.CopyToAsync(originalResponseBodyStream);
|
||||
//}
|
||||
//string responseBodyPayload = await ReadResponseBody(context.Response);
|
||||
//_diagnosticContext.Set("ResponseBody", responseBodyPayload);
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
}
|
||||
|
||||
private async Task<string> ReadRequestBody(HttpRequest request)
|
||||
{
|
||||
// Ensure the request's body can be read multiple times (for the next middlewares in the pipeline).
|
||||
request.EnableBuffering();
|
||||
|
||||
using var streamReader = new StreamReader(request.Body, leaveOpen: true);
|
||||
var requestBody = await streamReader.ReadToEndAsync();
|
||||
|
||||
// Reset the request's body stream position for next middleware in the pipeline.
|
||||
request.Body.Position = 0;
|
||||
return requestBody == null ? String.Empty : requestBody.Trim();
|
||||
}
|
||||
|
||||
private static async Task<string> ReadResponseBody(HttpResponse response)
|
||||
{
|
||||
response.Body.Seek(0, SeekOrigin.Begin);
|
||||
string responseBody = await new StreamReader(response.Body).ReadToEndAsync();
|
||||
response.Body.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
return $"{responseBody}";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
using Hangfire;
|
||||
using Hangfire.Dashboard;
|
||||
using IRaCIS.Core.API._PipelineExtensions.Serilog;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Serilog;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
|
||||
|
||||
public static class SerilogConfig
|
||||
{
|
||||
|
||||
public static void UseSerilogConfig(this IApplicationBuilder app, IWebHostEnvironment env)
|
||||
{
|
||||
app.UseMiddleware<RequestResponseLoggingMiddleware>();
|
||||
|
||||
app.UseSerilogRequestLogging(opts
|
||||
=>
|
||||
{
|
||||
opts.MessageTemplate = "{TokenUserRealName} {TokenUserType} {ClientIp} {RequestIP} {Host} {Protocol} {RequestMethod} {RequestPath} {RequestBody} responded {StatusCode} in {Elapsed:0.0000} ms";
|
||||
opts.EnrichDiagnosticContext = SerilogHelper.EnrichFromRequest;
|
||||
});
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Serilog;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
public class SerilogHelper
|
||||
{
|
||||
//public static string RequestPayload = "";
|
||||
|
||||
public static void EnrichFromRequest(IDiagnosticContext diagnosticContext, HttpContext httpContext)
|
||||
{
|
||||
var request = httpContext.Request;
|
||||
|
||||
// Set all the common properties available for every request
|
||||
diagnosticContext.Set("Host", request.Host);
|
||||
|
||||
//这种获取的Ip不准 配置服务才行
|
||||
diagnosticContext.Set("RequestIP", httpContext.Connection.RemoteIpAddress.ToString());
|
||||
|
||||
//这种方式可以,但是serilog提供了 就不用了
|
||||
//diagnosticContext.Set("TestIP", httpContext.GetUserIp());
|
||||
|
||||
diagnosticContext.Set("Protocol", request.Protocol);
|
||||
diagnosticContext.Set("Scheme", request.Scheme);
|
||||
|
||||
//这种方式不行 读取的body为空字符串 必须在中间件中读取
|
||||
//diagnosticContext.Set("RequestBody", await ReadRequestBody(httpContext.Request));
|
||||
//diagnosticContext.Set("RequestBody", RequestPayload);
|
||||
|
||||
// Only set it if available. You're not sending sensitive data in a querystring right?!
|
||||
if (request.QueryString.HasValue)
|
||||
{
|
||||
diagnosticContext.Set("QueryString", request.QueryString.Value);
|
||||
}
|
||||
|
||||
// Set the content-type of the Response at this point
|
||||
diagnosticContext.Set("ContentType", httpContext.Response.ContentType);
|
||||
|
||||
diagnosticContext.Set("TokenUserRealName", httpContext?.User?.FindFirst("realName")?.Value);
|
||||
|
||||
diagnosticContext.Set("TokenUserType", httpContext?.User?.FindFirst("userTypeEnumName")?.Value);
|
||||
|
||||
// Retrieve the IEndpointFeature selected for the request
|
||||
var endpoint = httpContext.GetEndpoint();
|
||||
if (endpoint is object) // endpoint != null
|
||||
{
|
||||
diagnosticContext.Set("EndpointName", endpoint.DisplayName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
public class ApiResponseHandler : AuthenticationHandler<AuthenticationSchemeOptions>
|
||||
{
|
||||
public ApiResponseHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
|
||||
{
|
||||
}
|
||||
|
||||
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
|
||||
{
|
||||
Response.ContentType = "application/json";
|
||||
Response.StatusCode = StatusCodes.Status401Unauthorized;
|
||||
await Response.WriteAsync(JsonConvert.SerializeObject(ResponseOutput.NotOk("您无权访问该接口")));
|
||||
}
|
||||
|
||||
protected override async Task HandleForbiddenAsync(AuthenticationProperties properties)
|
||||
{
|
||||
Response.ContentType = "application/json";
|
||||
Response.StatusCode = StatusCodes.Status403Forbidden;
|
||||
await Response.WriteAsync(JsonConvert.SerializeObject(ResponseOutput.NotOk("您的权限不允许进行该操作")));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
using IRaCIS.Core.Domain.Share;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
public static class AuthorizationPolicySetup
|
||||
{
|
||||
|
||||
public static void AddAuthorizationPolicySetup(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
services.AddAuthorization(options =>
|
||||
{
|
||||
//影像质控策略 只允许 CRC QA进行操作
|
||||
options.AddPolicy("ImageQCPolicy", policyBuilder =>
|
||||
{
|
||||
policyBuilder.RequireClaim("userTypeEnumInt", ((int)UserTypeEnum.ClinicalResearchCoordinator).ToString(), ((int)UserTypeEnum.IQC).ToString());
|
||||
});
|
||||
|
||||
//一致性核查策略 只允许 CRC PM APM 进行操作
|
||||
options.AddPolicy("ImageCheckPolicy", policyBuilder =>
|
||||
{
|
||||
policyBuilder.RequireClaim("userTypeEnumInt", ((int)UserTypeEnum.ProjectManager).ToString(), ((int)UserTypeEnum.ClinicalResearchCoordinator).ToString(), ((int)UserTypeEnum.APM).ToString());
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
using Invio.Extensions.Authentication.JwtBearer;
|
||||
using IRaCIS.Core.Application.Auth;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
public static class JWTAuthSetup
|
||||
{
|
||||
public static void AddJWTAuthSetup(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
services.Configure<JwtSetting>(configuration.GetSection("JwtSetting"));
|
||||
|
||||
var jwtSetting = new JwtSetting();
|
||||
configuration.Bind("JwtSetting", jwtSetting);
|
||||
|
||||
services
|
||||
.AddAuthentication(o=> {
|
||||
o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
o.DefaultChallengeScheme = nameof(ApiResponseHandler);
|
||||
o.DefaultForbidScheme = nameof(ApiResponseHandler);
|
||||
})
|
||||
.AddJwtBearer(options =>
|
||||
{
|
||||
options.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidIssuer = jwtSetting.Issuer,
|
||||
ValidAudience = jwtSetting.Audience,
|
||||
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSetting.SecurityKey)),
|
||||
// 默认 300s
|
||||
ClockSkew = TimeSpan.Zero
|
||||
};
|
||||
|
||||
// OPTION 1: use `Invio.Extensions.Authentication.JwtBearer`
|
||||
|
||||
options.AddQueryStringAuthentication();
|
||||
|
||||
// OPTION 2: do it manually
|
||||
|
||||
#region
|
||||
//options.Events = new JwtBearerEvents
|
||||
//{
|
||||
// OnMessageReceived = (context) => {
|
||||
|
||||
// if (!context.Request.Query.TryGetValue("access_token", out StringValues values))
|
||||
// {
|
||||
// return Task.CompletedTask;
|
||||
// }
|
||||
|
||||
// if (values.Count > 1)
|
||||
// {
|
||||
// context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
|
||||
// context.Fail(
|
||||
// "Only one 'access_token' query string parameter can be defined. " +
|
||||
// $"However, {values.Count:N0} were included in the request."
|
||||
// );
|
||||
|
||||
// return Task.CompletedTask;
|
||||
// }
|
||||
|
||||
// var token = values.Single();
|
||||
|
||||
// if (String.IsNullOrWhiteSpace(token))
|
||||
// {
|
||||
// context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
|
||||
// context.Fail(
|
||||
// "The 'access_token' query string parameter was defined, " +
|
||||
// "but a value to represent the token was not included."
|
||||
// );
|
||||
|
||||
// return Task.CompletedTask;
|
||||
// }
|
||||
|
||||
// context.Token = token;
|
||||
|
||||
// return Task.CompletedTask;
|
||||
// }
|
||||
//};
|
||||
#endregion
|
||||
|
||||
})
|
||||
.AddScheme<AuthenticationSchemeOptions, ApiResponseHandler>(nameof(ApiResponseHandler), o => { });
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
using AutoMapper.EquivalencyExpression;
|
||||
using IRaCIS.Core.Application.Service;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
public static class AutoMapperSetup
|
||||
{
|
||||
public static void AddAutoMapperSetup(this IServiceCollection services)
|
||||
{
|
||||
|
||||
services.AddAutoMapper(automapper =>
|
||||
{
|
||||
//AutoMapper.Collection.EntityFrameworkCore
|
||||
automapper.AddCollectionMappers();
|
||||
|
||||
#region 会使 IncludeMembers 失效 不能全局使用
|
||||
//mapping an EntityFramework Core DbContext-object.
|
||||
//automapper.UseEntityFrameworkCoreModel<IRaCISDBContext>(services);
|
||||
|
||||
|
||||
//automapper.ForAllMaps((a, b) => b.ForAllMembers(opt => opt.Condition((src, dest, srcMember, desMenber) =>
|
||||
//{
|
||||
// //// Can test When Guid? -> Guid if source is null will change to Guid.Empty
|
||||
// //Console.WriteLine("srcMember:" + srcMember + "desMenber:" + desMenber);
|
||||
// return srcMember != null && srcMember.ToString() != Guid.Empty.ToString();
|
||||
// // not want to map a null Guid? value to db Guid value
|
||||
//})));
|
||||
#endregion
|
||||
|
||||
}, typeof(QCConfig).Assembly);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
using Autofac;
|
||||
using Autofac.Extras.DynamicProxy;
|
||||
using IRaCIS.Application.Interfaces;
|
||||
using IRaCIS.Application.Services;
|
||||
using IRaCIS.Core.API.Utility.AOP;
|
||||
using IRaCIS.Core.Application;
|
||||
using IRaCIS.Core.Application.AOP;
|
||||
using IRaCIS.Core.Application.BackGroundJob;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using IRaCIS.Core.Infra.EFCore.AuthUser;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Panda.DynamicWebApi;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
// ReSharper disable once IdentifierTypo
|
||||
public class AutofacModuleSetup : Autofac.Module
|
||||
{
|
||||
protected override void Load(ContainerBuilder containerBuilder)
|
||||
{
|
||||
|
||||
#region byzhouhang 20210917 此处注册泛型仓储 可以减少Domain层 和Infra.EFcore 两层 空的仓储接口定义和 仓储文件定义
|
||||
|
||||
containerBuilder.RegisterGeneric(typeof(Repository<>))
|
||||
.As(typeof(IRepository<>)).InstancePerLifetimeScope();//注册泛型仓储
|
||||
|
||||
containerBuilder.RegisterGeneric(typeof(EFUnitOfWork<>))
|
||||
.As(typeof(IEFUnitOfWork<>)).InstancePerLifetimeScope();//注册仓储
|
||||
|
||||
#endregion
|
||||
|
||||
#region 指定控制器也由autofac 来进行实例获取 https://www.cnblogs.com/xwhqwer/p/15320838.html
|
||||
|
||||
//获取所有控制器类型并使用属性注入
|
||||
containerBuilder.RegisterAssemblyTypes(typeof(BaseService).Assembly)
|
||||
.Where(type => typeof(IDynamicWebApi).IsAssignableFrom(type))
|
||||
.PropertiesAutowired();
|
||||
|
||||
//var controllerBaseType = typeof(ControllerBase);
|
||||
//containerBuilder.RegisterAssemblyTypes(typeof(BaseService).Assembly)
|
||||
// .Where(t => controllerBaseType.IsAssignableFrom(t) && t != controllerBaseType)
|
||||
// .PropertiesAutowired();
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
Assembly application = Assembly.LoadFrom(AppDomain.CurrentDomain.BaseDirectory + "IRaCIS.Core.Application.dll");
|
||||
containerBuilder.RegisterAssemblyTypes(application).Where(t => t.FullName.Contains("Service"))
|
||||
.PropertiesAutowired().AsImplementedInterfaces().EnableClassInterceptors();
|
||||
|
||||
Assembly infrastructure = Assembly.Load("IRaCIS.Core.Infra.EFCore");
|
||||
containerBuilder.RegisterAssemblyTypes(infrastructure).AsImplementedInterfaces();
|
||||
|
||||
containerBuilder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>().SingleInstance();
|
||||
|
||||
|
||||
|
||||
containerBuilder.RegisterType<DicomFileStoreHelper>().SingleInstance();
|
||||
|
||||
//Autofac 注册拦截器 需要注意的是生成api上服务上的动态代理AOP失效 间接掉用不影响
|
||||
containerBuilder.RegisterType<TrialStatusAutofacAOP>();
|
||||
containerBuilder.RegisterType<UserAddAOP>();
|
||||
//containerBuilder.RegisterType<QANoticeAOP>();
|
||||
//containerBuilder.RegisterType<LogService>().As<ILogService>().SingleInstance();
|
||||
|
||||
|
||||
//注册hangfire任务 依赖注入
|
||||
containerBuilder.RegisterType<ObtainTaskAutoCancelJob>().As<IObtainTaskAutoCancelJob>().InstancePerDependency();
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
using FellowOakDicom;
|
||||
using FellowOakDicom.Imaging;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
public static class DicomSetup
|
||||
{
|
||||
public static void AddDicomSetup(this IServiceCollection services)
|
||||
{
|
||||
new DicomSetupBuilder()
|
||||
.RegisterServices(s => s.AddFellowOakDicom()
|
||||
.AddTranscoderManager<FellowOakDicom.Imaging.NativeCodec.NativeTranscoderManager>()
|
||||
.AddImageManager<ImageSharpImageManager>()
|
||||
)
|
||||
.SkipValidation()
|
||||
.Build();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Panda.DynamicWebApi;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
public static class DynamicWebApiSetup
|
||||
{
|
||||
//20210910 避免冗余的控制器层代码编写,仅仅包了一层前后台定义的格式 这里采用动态webAPi+IResultFilter 替代大部分情况
|
||||
public static void AddDynamicWebApiSetup(this IServiceCollection services)
|
||||
{
|
||||
//动态webApi 目前存在的唯一小坑是生成api上服务上的动态代理AOP失效 间接掉用不影响
|
||||
services.AddDynamicWebApi(dynamicWebApiOption =>
|
||||
{
|
||||
//默认是 api
|
||||
dynamicWebApiOption.DefaultApiPrefix = "";
|
||||
//首字母小写
|
||||
dynamicWebApiOption.GetRestFulActionName = (actionName) => char.ToLower(actionName[0]) + actionName.Substring(1);
|
||||
//删除 Service后缀
|
||||
dynamicWebApiOption.RemoveControllerPostfixes.Add("Service");
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
using IRaCIS.Core.Infra.EFCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
public static class EFSetup
|
||||
{
|
||||
public static void AddEFSetup( this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
//services.AddScoped<DbContext, IRaCISDBContext>();
|
||||
|
||||
//这个注入没有成功--注入是没问题的,构造函数也只是支持参数就好,错在注入的地方不能写DbContext
|
||||
//Web程序中通过重用池中DbContext实例可提高高并发场景下的吞吐量, 这在概念上类似于ADO.NET Provider原生的连接池操作方式,具有节省DbContext实例化成本的优点
|
||||
services.AddDbContext<IRaCISDBContext>(options =>
|
||||
{
|
||||
options.UseSqlServer(configuration.GetSection("ConnectionStrings:RemoteNew").Value,
|
||||
contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure());
|
||||
|
||||
options.EnableSensitiveDataLogging();
|
||||
|
||||
options.AddInterceptors(new QueryWithNoLockDbCommandInterceptor());
|
||||
|
||||
options.AddInterceptors(new AuditingInterceptor(configuration.GetSection("ConnectionStrings:RemoteNew").Value));
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
using EasyCaching.Core;
|
||||
using EasyCaching.Interceptor.Castle;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
public static class EasyCachingSetup
|
||||
{
|
||||
public static void AddEasyCachingSetup(this IServiceCollection services)
|
||||
{
|
||||
services.AddEasyCaching(options =>
|
||||
{
|
||||
options.UseInMemory();
|
||||
});
|
||||
services.ConfigureCastleInterceptor(options => options.CacheProviderName = EasyCachingConstValue.DefaultInMemoryName);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
using AspNetCoreRateLimit;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
/// <summary>
|
||||
/// IPLimit限流 启动服务
|
||||
/// </summary>
|
||||
public static class IpPolicyRateLimitSetup
|
||||
{
|
||||
public static void AddIpPolicyRateLimitSetup(this IServiceCollection services, IConfiguration Configuration)
|
||||
{
|
||||
|
||||
// needed to store rate limit counters and ip rules
|
||||
services.AddMemoryCache();
|
||||
|
||||
//load general configuration from appsettings.json
|
||||
services.Configure<IpRateLimitOptions>(Configuration.GetSection("IpRateLimiting"));
|
||||
|
||||
//load ip rules from appsettings.json
|
||||
//services.Configure<IpRateLimitPolicies>(Configuration.GetSection("IpRateLimitPolicies"));
|
||||
|
||||
// inject counter and rules stores
|
||||
services.AddInMemoryRateLimiting();
|
||||
//services.AddDistributedRateLimiting<AsyncKeyLockProcessingStrategy>();
|
||||
//services.AddDistributedRateLimiting<RedisProcessingStrategy>();
|
||||
//services.AddRedisRateLimiting();
|
||||
|
||||
|
||||
|
||||
// configuration (resolvers, counter key builders)
|
||||
services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
|
||||
using LogDashboard;
|
||||
using LogDashboard.Authorization.Filters;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
public static class LogDashboardSetup
|
||||
{
|
||||
public static void AddLogDashboardSetup(this IServiceCollection services)
|
||||
{
|
||||
//IIS 配置虚拟路径部署,会出现IIS静态文件404
|
||||
services.AddLogDashboard(opt =>
|
||||
{
|
||||
//opt.PathMatch = "/api/LogDashboard";
|
||||
opt.PathMatch = "/LogDashboard";
|
||||
|
||||
//opt.AddAuthorizationFilter(new LogDashboardBasicAuthFilter("admin", "zhizhun2018"));
|
||||
|
||||
//opt.AddAuthorizationFilter(new LogDashBoardAuthFilter());
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
//using System;
|
||||
//using Microsoft.Extensions.DependencyInjection;
|
||||
//using StackExchange.Profiling.Storage;
|
||||
|
||||
//namespace IRaCIS.Core.API
|
||||
//{
|
||||
// public class MiniProfilerConfigure
|
||||
// {
|
||||
// public static void ConfigureMiniProfiler(IServiceCollection services)
|
||||
// {
|
||||
|
||||
// services.AddMiniProfiler(options =>
|
||||
// {
|
||||
// // All of this is optional. You can simply call .AddMiniProfiler() for all defaults
|
||||
|
||||
// // (Optional) Path to use for profiler URLs, default is /mini-profiler-resources
|
||||
// options.RouteBasePath = "/profiler";
|
||||
|
||||
// //// (Optional) Control storage
|
||||
// //// (default is 30 minutes in MemoryCacheStorage)
|
||||
// (options.Storage as MemoryCacheStorage).CacheDuration = TimeSpan.FromMinutes(10);
|
||||
|
||||
// //// (Optional) Control which SQL formatter to use, InlineFormatter is the default
|
||||
// //options.SqlFormatter = new StackExchange.Profiling.SqlFormatters.InlineFormatter();
|
||||
|
||||
// //// (Optional) To control authorization, you can use the Func<HttpRequest, bool> options:
|
||||
// //// (default is everyone can access profilers)
|
||||
// //options.ResultsAuthorize = request => MyGetUserFunction(request).CanSeeMiniProfiler;
|
||||
// //options.ResultsListAuthorize = request => MyGetUserFunction(request).CanSeeMiniProfiler;
|
||||
// //// Or, there are async versions available:
|
||||
// //options.ResultsAuthorizeAsync = async request => (await MyGetUserFunctionAsync(request)).CanSeeMiniProfiler;
|
||||
// //options.ResultsAuthorizeListAsync = async request => (await MyGetUserFunctionAsync(request)).CanSeeMiniProfilerLists;
|
||||
|
||||
// //// (Optional) To control which requests are profiled, use the Func<HttpRequest, bool> option:
|
||||
// //// (default is everything should be profiled)
|
||||
// //options.ShouldProfile = request => MyShouldThisBeProfiledFunction(request);
|
||||
|
||||
// //// (Optional) Profiles are stored under a user ID, function to get it:
|
||||
// //// (default is null, since above methods don't use it by default)
|
||||
// //options.UserIdProvider = request => MyGetUserIdFunction(request);
|
||||
|
||||
// //// (Optional) Swap out the entire profiler provider, if you want
|
||||
// //// (default handles async and works fine for almost all applications)
|
||||
// //options.ProfilerProvider = new MyProfilerProvider();
|
||||
|
||||
// //// (Optional) You can disable "Connection Open()", "Connection Close()" (and async variant) tracking.
|
||||
// //// (defaults to true, and connection opening/closing is tracked)
|
||||
// //options.TrackConnectionOpenClose = true;
|
||||
|
||||
// //// (Optional) Use something other than the "light" color scheme.
|
||||
// //// (defaults to "light")
|
||||
// //options.ColorScheme = StackExchange.Profiling.ColorScheme.Auto;
|
||||
|
||||
// //// The below are newer options, available in .NET Core 3.0 and above:
|
||||
|
||||
// //// (Optional) You can disable MVC filter profiling
|
||||
// //// (defaults to true, and filters are profiled)
|
||||
// //options.EnableMvcFilterProfiling = true;
|
||||
// //// ...or only save filters that take over a certain millisecond duration (including their children)
|
||||
// //// (defaults to null, and all filters are profiled)
|
||||
// //// options.MvcFilterMinimumSaveMs = 1.0m;
|
||||
|
||||
// //// (Optional) You can disable MVC view profiling
|
||||
// //// (defaults to true, and views are profiled)
|
||||
// //options.EnableMvcViewProfiling = true;
|
||||
// //// ...or only save views that take over a certain millisecond duration (including their children)
|
||||
// //// (defaults to null, and all views are profiled)
|
||||
// //// options.MvcViewMinimumSaveMs = 1.0m;
|
||||
|
||||
// //// (Optional) listen to any errors that occur within MiniProfiler itself
|
||||
// //// options.OnInternalError = e => MyExceptionLogger(e);
|
||||
|
||||
// //// (Optional - not recommended) You can enable a heavy debug mode with stacks and tooltips when using memory storage
|
||||
// //// It has a lot of overhead vs. normal profiling and should only be used with that in mind
|
||||
// //// (defaults to false, debug/heavy mode is off)
|
||||
// ////options.EnableDebugMode = true;
|
||||
// });
|
||||
// //.AddEntityFramework();
|
||||
// }
|
||||
// }
|
||||
//}
|
|
@ -0,0 +1,46 @@
|
|||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
public static class NewtonsoftJsonSetup
|
||||
{
|
||||
public static void AddNewtonsoftJsonSetup(this IMvcBuilder builder)
|
||||
{
|
||||
|
||||
builder.AddNewtonsoftJson(options =>
|
||||
{
|
||||
//options.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
|
||||
// 忽略循环引用
|
||||
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
|
||||
//options.SerializerSettings.TypeNameHandling = TypeNameHandling.All;
|
||||
|
||||
//处理返回给前端 可空类型 给出默认值 比如in? 为null 设置 默认值0
|
||||
options.SerializerSettings.ContractResolver = new NullToEmptyStringResolver(); //new DefaultContractResolver();// new NullToEmptyStringResolver();
|
||||
// 设置时间格式
|
||||
options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
//options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
|
||||
|
||||
}).AddControllersAsServices()//动态webApi属性注入需要
|
||||
.ConfigureApiBehaviorOptions(o =>
|
||||
{
|
||||
o.SuppressModelStateInvalidFilter = true; //自己写验证
|
||||
|
||||
////这里是自定义验证结果和返回状态码 因为这里是在[ApiController]控制器层校验,动态webApi的不会校验 所以需要单独写一个Filter
|
||||
//o.InvalidModelStateResponseFactory = (context) =>
|
||||
//{
|
||||
// var error = context.ModelState .Keys
|
||||
// .SelectMany(k => context.ModelState[k].Errors)
|
||||
// .Select(e => e.ErrorMessage)
|
||||
// .ToArray();
|
||||
|
||||
//return new JsonResult(ResponseOutput.NotOk("The inputs supplied to the API are invalid. " + JsonConvert.SerializeObject( error)));
|
||||
//};
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
public class NullToEmptyStringResolver : DefaultContractResolver
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建属性
|
||||
/// </summary>
|
||||
/// <param name="type">类型</param>
|
||||
/// <param name="memberSerialization">序列化成员</param>
|
||||
/// <returns></returns>
|
||||
//protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
|
||||
//{
|
||||
// IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
|
||||
|
||||
|
||||
// foreach (var jsonProperty in properties)
|
||||
// {
|
||||
// jsonProperty.DefaultValue = new NullToEmptyStringValueProvider(jsonProperty);
|
||||
// }
|
||||
|
||||
// return properties;
|
||||
|
||||
//}
|
||||
|
||||
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
|
||||
{
|
||||
IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
|
||||
|
||||
var list= type.GetProperties()
|
||||
.Select(p =>
|
||||
{
|
||||
var jp = base.CreateProperty(p, memberSerialization);
|
||||
jp.ValueProvider = new NullToEmptyStringValueProvider(p);
|
||||
return jp;
|
||||
}).ToList();
|
||||
|
||||
var uu = list.Select(t => t.PropertyName).ToList();
|
||||
|
||||
//获取复杂对象属性
|
||||
properties = properties.TakeWhile(t => !uu.Contains(t.PropertyName)).ToList();
|
||||
|
||||
list.AddRange(properties);
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
|
||||
public class NullToEmptyStringValueProvider : IValueProvider
|
||||
{
|
||||
PropertyInfo _MemberInfo;
|
||||
public NullToEmptyStringValueProvider(PropertyInfo memberInfo)
|
||||
{
|
||||
_MemberInfo = memberInfo;
|
||||
}
|
||||
public object GetValue(object target)
|
||||
{
|
||||
object result = _MemberInfo.GetValue(target);
|
||||
if (_MemberInfo.PropertyType == typeof(string) && result == null) result = "";
|
||||
else if (_MemberInfo.PropertyType == typeof(String[]) && result == null) result = new string[] { };
|
||||
//else if (_MemberInfo.PropertyType == typeof(Nullable<Int32>) && result == null) result = 0;
|
||||
else if (_MemberInfo.PropertyType == typeof(Nullable<Decimal>) && result == null) result = 0.00M;
|
||||
|
||||
return result;
|
||||
}
|
||||
public void SetValue(object target, object value)
|
||||
{
|
||||
|
||||
if(_MemberInfo.PropertyType == typeof(string))
|
||||
{
|
||||
//去掉前后空格
|
||||
_MemberInfo.SetValue(target, value==null?string.Empty: value.ToString()==string.Empty? value:value.ToString().Trim());
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
_MemberInfo.SetValue(target, value);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
public static class QuartZSetup
|
||||
{
|
||||
public static void AddQuartZSetup(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
//services.AddTransient<CacheTrialStatusQuartZJob>();
|
||||
|
||||
//services.AddQuartz(q =>
|
||||
//{
|
||||
// // base quartz scheduler, job and trigger configuration
|
||||
|
||||
// // as of 3.3.2 this also injects scoped services (like EF DbContext) without problems
|
||||
// q.UseMicrosoftDependencyInjectionJobFactory();
|
||||
|
||||
// // 基本Quartz调度器、作业和触发器配置
|
||||
// var jobKey = new JobKey("RegularTrialWork", "regularWorkGroup");
|
||||
// q.AddJob<CacheTrialStatusQuartZJob>(jobKey, j => j
|
||||
// .WithDescription("Trial regular work")
|
||||
// );
|
||||
// q.AddTrigger(t => t
|
||||
// .WithIdentity("TrialStatusTrigger")
|
||||
// .ForJob(jobKey)
|
||||
// //.StartNow()
|
||||
// //.WithSimpleSchedule(x => x.WithInterval(TimeSpan.FromSeconds(15))//开始秒数 15s
|
||||
// // .RepeatForever())//持续工作
|
||||
// .WithCronSchedule("0 0 0/2 * * ?")//每小时执行一次
|
||||
// .WithDescription("My regular trial work trigger")
|
||||
// );
|
||||
//});
|
||||
|
||||
//// ASP.NET Core hosting
|
||||
//services.AddQuartzServer(options =>
|
||||
//{
|
||||
// // when shutting down we want jobs to complete gracefully
|
||||
// options.WaitForJobsToComplete = true;
|
||||
//});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.ResponseCompression;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
public static class ResponseCompressionSetup
|
||||
{
|
||||
public static void AddResponseCompressionSetup(this IServiceCollection services)
|
||||
{
|
||||
services.AddResponseCompression(options =>
|
||||
{
|
||||
options.Providers.Add<BrotliCompressionProvider>();
|
||||
options.Providers.Add<GzipCompressionProvider>();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using Serilog;
|
||||
using Serilog.Configuration;
|
||||
using Serilog.Core;
|
||||
using Serilog.Events;
|
||||
using System;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
public static class EnricherExtensions
|
||||
{
|
||||
public static LoggerConfiguration WithHttpContextInfo(this LoggerEnrichmentConfiguration enrich, IServiceProvider serviceProvider)
|
||||
{
|
||||
if (enrich == null)
|
||||
throw new ArgumentNullException(nameof(enrich));
|
||||
|
||||
return enrich.With(new HttpContextEnricher(serviceProvider));
|
||||
}
|
||||
public static LoggerConfiguration WithHttpContextInfo(this LoggerEnrichmentConfiguration enrich, IServiceProvider serviceProvider, Action<LogEvent, ILogEventPropertyFactory, HttpContext> enrichAction)
|
||||
{
|
||||
if (enrich == null)
|
||||
throw new ArgumentNullException(nameof(enrich));
|
||||
|
||||
return enrich.With(new HttpContextEnricher(serviceProvider, enrichAction));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
using IRaCIS.Core.Infra.EFCore.AuthUser;
|
||||
using IRaCIS.Core.Infrastructure;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Serilog.Core;
|
||||
using Serilog.Events;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
public class HttpContextEnricher : ILogEventEnricher
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly Action<LogEvent, ILogEventPropertyFactory, HttpContext> _enrichAction;
|
||||
|
||||
public HttpContextEnricher(IServiceProvider serviceProvider) : this(serviceProvider, null)
|
||||
{ }
|
||||
|
||||
public HttpContextEnricher(IServiceProvider serviceProvider, Action<LogEvent, ILogEventPropertyFactory, HttpContext> enrichAction)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
if (enrichAction == null)
|
||||
{
|
||||
_enrichAction = (logEvent, propertyFactory, httpContext) =>
|
||||
{
|
||||
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("RequestIP", httpContext.Connection.RemoteIpAddress.ToString()));
|
||||
|
||||
//这样读取没用
|
||||
//logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("RequestBody", await ReadRequestBody(httpContext.Request)));
|
||||
//logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("RequestIP", IPHelper.GetIP(httpContext.Request) ));
|
||||
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("TokenUserRealName", httpContext?.User?.FindFirst(ClaimAttributes.RealName)?.Value));
|
||||
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("TokenUserType", httpContext?.User?.FindFirst("userTypeEnumName")?.Value));
|
||||
|
||||
//logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("Referer", httpContext.Request.Headers["Referer"].ToString()));
|
||||
//logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("request_path", httpContext.Request.Path));
|
||||
//logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("request_method", httpContext.Request.Method));
|
||||
//if (httpContext.Response.HasStarted)
|
||||
//{
|
||||
// logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("response_status", httpContext.Response.StatusCode));
|
||||
//}
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
_enrichAction = enrichAction;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
|
||||
{
|
||||
var httpContext = _serviceProvider.GetService<IHttpContextAccessor>()?.HttpContext;
|
||||
if (null != httpContext)
|
||||
{
|
||||
_enrichAction.Invoke(logEvent, propertyFactory, httpContext);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string> ReadRequestBody(HttpRequest request)
|
||||
{
|
||||
// Ensure the request's body can be read multiple times (for the next middlewares in the pipeline).
|
||||
request.EnableBuffering();
|
||||
|
||||
using var streamReader = new StreamReader(request.Body, leaveOpen: true);
|
||||
var requestBody = await streamReader.ReadToEndAsync();
|
||||
|
||||
// Reset the request's body stream position for next middleware in the pipeline.
|
||||
request.Body.Position = 0;
|
||||
return requestBody==null?String.Empty: requestBody.Trim();
|
||||
}
|
||||
|
||||
private async Task<string> ReadResponseBody(HttpResponse response)
|
||||
{
|
||||
response.Body.Seek(0, SeekOrigin.Begin);
|
||||
string responseBody = await new StreamReader(response.Body).ReadToEndAsync();
|
||||
response.Body.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
return $"{responseBody}";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
using Microsoft.AspNetCore.Builder;
|
||||
using Serilog;
|
||||
using Serilog.Events;
|
||||
using Serilog.Sinks.Email;
|
||||
using System;
|
||||
using System.Net;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
public class SerilogExtension
|
||||
{
|
||||
|
||||
public static void AddSerilogSetup(string environment, IServiceProvider serviceProvider)
|
||||
{
|
||||
|
||||
var config = new LoggerConfiguration()
|
||||
.MinimumLevel.Information()
|
||||
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
|
||||
// Filter out ASP.NET Core infrastructre logs that are Information and below 日志太多了 一个请求 记录好几条
|
||||
.MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)
|
||||
.MinimumLevel.Override("Hangfire", LogEventLevel.Warning)
|
||||
.MinimumLevel.Override("System.Net.Http.HttpClient.HttpReports", LogEventLevel.Warning)
|
||||
.Enrich.WithClientIp()
|
||||
.Enrich.WithClientAgent()
|
||||
.Enrich.FromLogContext()
|
||||
|
||||
//控制台 方便调试 问题 我们显示记录日志 时 获取上下文的ip 和用户名 用户类型
|
||||
.WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Warning,
|
||||
outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3} ] {ClientIp} {TokenUserRealName} {TokenUserType} {Message:lj} {Properties:j}{NewLine} {Exception}")
|
||||
.WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day,
|
||||
outputTemplate: "{Timestamp:HH:mm:ss} || {Level} || {SourceContext:l} || {Message} ||{Exception} ||end {NewLine}");
|
||||
//.WriteTo.MSSqlServer("Data Source=DESKTOP-4TU9A6M;Initial Catalog=CoreFrame;User ID=sa;Password=123456", "logs", autoCreateSqlTable: true, restrictedToMinimumLevel: LogEventLevel.Information)//从左至右四个参数分别是数据库连接字符串、表名、如果表不存在是否创建、最低等级。Serilog会默认创建一些列。
|
||||
|
||||
if (environment == "Production")
|
||||
{
|
||||
config.WriteTo.Email(new EmailConnectionInfo()
|
||||
{
|
||||
EmailSubject = "系统警告,请速速查看!",//邮件标题
|
||||
FromEmail = "iracis_grr@163.com",//发件人邮箱
|
||||
MailServer = "smtp.163.com",//smtp服务器地址
|
||||
NetworkCredentials = new NetworkCredential("iracis_grr@163.com", "XLWVQKZAEKLDWOAH"),//两个参数分别是发件人邮箱与客户端授权码
|
||||
Port = 25,//端口号
|
||||
ToEmail = "872297557@qq.com"//收件人
|
||||
}, restrictedToMinimumLevel: LogEventLevel.Error,
|
||||
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [ {Level} {ClientIp} {ClientAgent} {TokenUserRealName} {TokenUserType} ] || [path: {RequestPath} arguments: {RequestBody}] {SourceContext:l} || {Message} || {Exception} ||end {NewLine})");
|
||||
}
|
||||
|
||||
//扩展方法 获取上下文的ip 用户名 用户类型
|
||||
Log.Logger = config.Enrich.WithHttpContextInfo(serviceProvider).CreateLogger();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
public static class StaticFileAuthorizationSetup
|
||||
{
|
||||
public static void AddStaticFileAuthorizationSetup(this IServiceCollection services)
|
||||
{
|
||||
services.AddAuthorization(options =>
|
||||
{
|
||||
options.FallbackPolicy = new AuthorizationPolicyBuilder()
|
||||
.RequireAuthenticatedUser()
|
||||
.Build();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
using Microsoft.OpenApi.Models;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
public class JsonPatchDocumentFilter : IDocumentFilter
|
||||
{
|
||||
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
|
||||
{
|
||||
var schemas = swaggerDoc.Components.Schemas.ToList();
|
||||
foreach (var item in schemas)
|
||||
{
|
||||
if (item.Key.StartsWith("Operation") || item.Key.StartsWith("JsonPatchDocument"))
|
||||
swaggerDoc.Components.Schemas.Remove(item.Key);
|
||||
}
|
||||
|
||||
swaggerDoc.Components.Schemas.Add("Operation", new OpenApiSchema
|
||||
{
|
||||
Type = "object",
|
||||
Properties = new Dictionary<string, OpenApiSchema>
|
||||
{
|
||||
{ "op", new OpenApiSchema { Type = "string" } },
|
||||
{"value", new OpenApiSchema{ Type = "object", Nullable = true } },
|
||||
{ "path", new OpenApiSchema { Type = "string" } }
|
||||
}
|
||||
});
|
||||
|
||||
swaggerDoc.Components.Schemas.Add("JsonPatchDocument", new OpenApiSchema
|
||||
{
|
||||
Type = "array",
|
||||
Items = new OpenApiSchema
|
||||
{
|
||||
Reference = new OpenApiReference { Type = ReferenceType.Schema, Id = "Operation" }
|
||||
},
|
||||
Description = "Array of operations to perform"
|
||||
});
|
||||
|
||||
foreach (var path in swaggerDoc.Paths.SelectMany(p => p.Value.Operations)
|
||||
.Where(p => p.Key == Microsoft.OpenApi.Models.OperationType.Patch))
|
||||
{
|
||||
foreach (var item in path.Value.RequestBody.Content.Where(c => c.Key != "application/json-patch+json"))
|
||||
path.Value.RequestBody.Content.Remove(item.Key);
|
||||
|
||||
var response = path.Value.RequestBody.Content.SingleOrDefault(c => c.Key == "application/json-patch+json");
|
||||
|
||||
response.Value.Schema = new OpenApiSchema
|
||||
{
|
||||
Reference = new OpenApiReference { Type = ReferenceType.Schema, Id = "JsonPatchDocument" }
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
using IRaCIS.Core.Application.Contracts;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Swashbuckle.AspNetCore.Filters;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using Swashbuckle.AspNetCore.SwaggerUI;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
public static class SwaggerSetup
|
||||
{
|
||||
public static void AddSwaggerSetup(this IServiceCollection services)
|
||||
{
|
||||
services.AddSwaggerExamplesFromAssemblyOf<JsonPatchUserRequestExample>();
|
||||
|
||||
services.AddSwaggerGen(options =>
|
||||
{
|
||||
//此处的Name 是控制器上分组的名称 Title是界面的大标题
|
||||
//分组
|
||||
options.SwaggerDoc("Reviewer", new OpenApiInfo {Title = "医生模块",Version = "Reviewer", });
|
||||
options.SwaggerDoc("Trial", new OpenApiInfo { Title = "项目模块", Version = "Trial" });
|
||||
options.SwaggerDoc("Enroll", new OpenApiInfo { Title = "入组模块", Version = "Enroll" });
|
||||
options.SwaggerDoc("Workload", new OpenApiInfo { Title = "工作量模块", Version = "Workload" });
|
||||
options.SwaggerDoc("Common", new OpenApiInfo { Title = "通用信息获取", Version = "Common" });
|
||||
options.SwaggerDoc("Institution", new OpenApiInfo { Title = "机构信息模块", Version = "Institution" });
|
||||
options.SwaggerDoc("Dashboard&Statistics", new OpenApiInfo { Title = "统计模块", Version = "Dashboard&Statistics" });
|
||||
|
||||
options.SwaggerDoc("Financial", new OpenApiInfo { Title = "财务模块", Version = "Financial" });
|
||||
options.SwaggerDoc("Management", new OpenApiInfo { Title = "管理模块", Version = "Management" });
|
||||
options.SwaggerDoc("Image", new OpenApiInfo { Title = "影像模块", Version = "Image" });
|
||||
options.SwaggerDoc("Reading", new OpenApiInfo { Title = "读片模块", Version = "Reading" });
|
||||
|
||||
// 接口排序
|
||||
options.OrderActionsBy(o => o.GroupName);
|
||||
|
||||
options.DocInclusionPredicate((docName, apiDes) =>
|
||||
{
|
||||
if (!apiDes.TryGetMethodInfo(out MethodInfo methodInfo)) return false;
|
||||
var versions = methodInfo.DeclaringType.GetCustomAttributes(true)
|
||||
.OfType<ApiExplorerSettingsAttribute>()
|
||||
.Select(attr => attr.GroupName);
|
||||
|
||||
return versions.Any(v => v.ToString() == docName);
|
||||
});
|
||||
|
||||
var xmlPath = Path.Combine(AppContext.BaseDirectory, "IRaCIS.Core.API.xml");//这个就是刚刚配置的xml文件名
|
||||
options.IncludeXmlComments(xmlPath, true);
|
||||
|
||||
var xmlPath2 = Path.Combine(AppContext.BaseDirectory, "IRaCIS.Core.Application.xml");//这个就是刚刚配置的xml文件名
|
||||
options.IncludeXmlComments(xmlPath2, true);
|
||||
//默认的第二个参数是false,这个是controller的注释,记得修改
|
||||
|
||||
|
||||
// 在header中添加token,传递到后台
|
||||
options.OperationFilter<SecurityRequirementsOperationFilter>();
|
||||
|
||||
options.DocumentFilter<JsonPatchDocumentFilter>();
|
||||
|
||||
// 添加登录按钮
|
||||
options.AddSecurityDefinition("bearerAuth", new OpenApiSecurityScheme()
|
||||
{
|
||||
Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
|
||||
Name = "Authorization",
|
||||
|
||||
//In = "header",
|
||||
//Type = "apiKey"
|
||||
});
|
||||
|
||||
|
||||
//// Bearer
|
||||
//options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
|
||||
//{
|
||||
// Description = "JWT Authorization header using the Bearer scheme.",
|
||||
// Name = "Authorization",
|
||||
// In = ParameterLocation.Header,
|
||||
// Scheme = "bearer",
|
||||
// Type = SecuritySchemeType.Http,
|
||||
// BearerFormat = "JWT"
|
||||
//});
|
||||
});
|
||||
}
|
||||
|
||||
public static void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI(options =>
|
||||
{
|
||||
//此处的Name 是页面 选择文档下拉框 显示的名称
|
||||
options.SwaggerEndpoint($"swagger/Reviewer/swagger.json", "医生模块");
|
||||
options.SwaggerEndpoint($"swagger/Trial/swagger.json", "项目模块");
|
||||
options.SwaggerEndpoint($"swagger/Enroll/swagger.json", "入组模块");
|
||||
options.SwaggerEndpoint($"swagger/Workload/swagger.json", "工作量模块");
|
||||
options.SwaggerEndpoint($"swagger/Dashboard&Statistics/swagger.json", "统计模块");
|
||||
options.SwaggerEndpoint($"swagger/Common/swagger.json", "通用模块");
|
||||
|
||||
options.SwaggerEndpoint($"swagger/Financial/swagger.json", "财务模块");
|
||||
options.SwaggerEndpoint($"swagger/Institution/swagger.json", "机构信息模块");
|
||||
options.SwaggerEndpoint($"swagger/Management/swagger.json", "管理模块");
|
||||
options.SwaggerEndpoint($"swagger/Image/swagger.json", "影像模块");
|
||||
options.SwaggerEndpoint($"swagger/Reading/swagger.json", "读片模块");
|
||||
|
||||
//路径配置,设置为空,表示直接在根域名(localhost:8001)访问该文件,
|
||||
//注意localhost:8001/swagger是访问不到的,去launchSettings.json把launchUrl去掉,如果你想换一个路径,直接写名字即可,比如直接写c.Route = "doc";
|
||||
//options.RoutePrefix = string.Empty;
|
||||
|
||||
var data = Assembly.GetExecutingAssembly().Location;
|
||||
options.IndexStream = () => Assembly.GetExecutingAssembly()
|
||||
.GetManifestResourceStream("IRaCIS.Core.API.wwwroot.swagger.ui.Index.html");
|
||||
|
||||
options.RoutePrefix = string.Empty;
|
||||
|
||||
//DocExpansion设置为none可折叠所有方法
|
||||
options.DocExpansion(DocExpansion.None);
|
||||
//DefaultModelsExpandDepth设置为 - 1 可不显示models
|
||||
options.DefaultModelsExpandDepth(-1);
|
||||
|
||||
|
||||
// 引入静态文件添加登录功能
|
||||
// 清除静态文件缓存
|
||||
// options.IndexStream = () => null;
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
using Hangfire;
|
||||
using Hangfire.SqlServer;
|
||||
using Hangfire.Tags.SqlServer;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
|
||||
namespace IRaCIS.Core.API
|
||||
{
|
||||
public static class hangfireSetup
|
||||
{
|
||||
public static void AddhangfireSetup(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
var hangFireConnStr = configuration.GetSection("ConnectionStrings:Hangfire").Value;
|
||||
|
||||
services.AddHangfire(hangFireConfig =>
|
||||
{
|
||||
//指定存储介质
|
||||
hangFireConfig.UseSqlServerStorage(hangFireConnStr, new SqlServerStorageOptions()
|
||||
{
|
||||
SchemaName = "hangfire",
|
||||
CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
|
||||
SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
|
||||
QueuePollInterval = TimeSpan.Zero,
|
||||
UseRecommendedIsolationLevel = true,
|
||||
UsePageLocksOnDequeue = true,
|
||||
DisableGlobalLocks = true
|
||||
});
|
||||
|
||||
hangFireConfig.UseTagsWithSql(); //nuget引入Hangfire.Tags.SqlServer
|
||||
//.UseHangfireHttpJob();
|
||||
|
||||
});
|
||||
|
||||
services.AddHangfireServer();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=123.56.181.144,14333\\MSSQLExpress14;Database=IRaCIS_New;User ID=sa;Password=dev123456DEV;"
|
||||
//,"hang": "Server=ZHOU;Database=IRaCIS;User ID=sa;Password=sa123456;",
|
||||
|
||||
},
|
||||
"HttpReports": {
|
||||
"Transport": {
|
||||
"CollectorAddress": "http://123.56.181.144:6099/",
|
||||
"DeferSecond": 10,
|
||||
"DeferThreshold": 100
|
||||
},
|
||||
"Server": "http://localhost:6100/",
|
||||
"Service": "IRaCIS",
|
||||
"Switch": true,
|
||||
//"RequestFilter": [ "/api/health/*", "/HttpReports*" ],
|
||||
"WithRequest": true,
|
||||
"WithResponse": true,
|
||||
"WithCookie": false,
|
||||
"WithHeader": true
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
//"RootUrl": "http://123.56.181.144:8060/",
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=123.56.181.144,14333\\MSSQLExpress14;Database=IRaCIS_New;User ID=sa;Password=dev123456DEV;",
|
||||
"Remote8099": "Server=123.56.181.144,14333\\MSSQLExpress14;Database=IRaCIS_Test;User ID=sa;Password=dev123456DEV;"
|
||||
},
|
||||
"HttpReports": {
|
||||
"Transport": {
|
||||
"CollectorAddress": "http://123.56.181.144:6099/",
|
||||
"DeferSecond": 10,
|
||||
"DeferThreshold": 100
|
||||
},
|
||||
"Server": "http://123.56.181.144:8060/",
|
||||
"Service": "IRaCIS_8060",
|
||||
"Switch": true,
|
||||
//"RequestFilter": [ "/api/health/*", "/HttpReports*" ],
|
||||
"WithRequest": true,
|
||||
"WithResponse": true,
|
||||
"WithCookie": false,
|
||||
"WithHeader": true
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"RemoteNew": "Server=123.56.181.144,14333\\MSSQLExpress14;Database=IRaCIS_New;User ID=sa;Password=dev123456DEV;"
|
||||
},
|
||||
"HttpReports": {
|
||||
"Transport": {
|
||||
"CollectorAddress": "http://123.56.181.144:6099/",
|
||||
"DeferSecond": 10,
|
||||
"DeferThreshold": 100
|
||||
},
|
||||
"Server": "http://192.168.1.2:8000/",
|
||||
"Service": "IRaCIS_1.2_8000",
|
||||
"Switch": true,
|
||||
//"RequestFilter": [ "/api/health/*", "/HttpReports*" ],
|
||||
"WithRequest": true,
|
||||
"WithResponse": true,
|
||||
"WithCookie": false,
|
||||
"WithHeader": true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
{
|
||||
"ConnectionStrings": {
|
||||
"hang": "Server=ZHOU;Database=IRaCIS;User ID=sa;Password=sa123456;",
|
||||
"Hangfire": "Server=123.56.181.144,14333\\MSSQLExpress14;Database=Hangfire_IRaCIS;User ID=sa;Password=dev123456DEV;"
|
||||
},
|
||||
"JwtSetting": {
|
||||
"SecurityKey": "3e6dbfa227234a03977a2f421bdb7f4f", // 密钥
|
||||
"Issuer": "IRaCIS", // 颁发者
|
||||
"Audience": "ZhiZhun", // 接收者
|
||||
"TokenExpireDays": "7" // 过期时间(7day)
|
||||
},
|
||||
"IpRateLimiting": {
|
||||
"EnableEndpointRateLimiting": true, //False: globally executed, true: executed for each
|
||||
"StackBlockedRequests": false, // set to false, rejected calls are not added to the throttle counter
|
||||
"RealIpHeader": "X-Real-IP",
|
||||
"ClientIdHeader": "X-ClientId",
|
||||
"QuotaExceededResponse": {
|
||||
"Content": "{{\"ErrorMessage\":\"The program performs flow limiting,Your requests are too frequent, please try again later, or contact the administrator to modify the IP flow restriction rules\",\"IsSuccess\":false}}",
|
||||
"ContentType": "application/json",
|
||||
"StatusCode": 429
|
||||
},
|
||||
"HttpStatusCode": 429,
|
||||
//"IpWhitelist": [ "127.0.0.1", "::1/10", "192.168.0.0/24" ],
|
||||
"EndpointWhitelist": [ "post:/study/archivestudy/*" ],
|
||||
//"EndpointWhitelist": [ ],
|
||||
//"EndpointWhitelist": ["post:/trial/getTrialList"], //demo
|
||||
"IpWhitelist": [],
|
||||
//"ClientWhitelist": [ "dev-id-1", "dev-id-2" ],
|
||||
"GeneralRules": [
|
||||
{
|
||||
"Endpoint": "*",
|
||||
"Period": "1s",
|
||||
"Limit": 3
|
||||
},
|
||||
{
|
||||
"Endpoint": "*",
|
||||
"Period": "15m",
|
||||
"Limit": 100
|
||||
},
|
||||
{
|
||||
"Endpoint": "*",
|
||||
"Period": "12h",
|
||||
"Limit": 1000
|
||||
},
|
||||
{
|
||||
"Endpoint": "*",
|
||||
"Period": "7d",
|
||||
"Limit": 10000
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"easycaching": {
|
||||
"inmemory": {
|
||||
"MaxRdSecond": 120,
|
||||
"EnableLogging": false,
|
||||
"LockMs": 5000,
|
||||
"SleepMs": 300,
|
||||
"DBConfig": {
|
||||
"SizeLimit": 10000,
|
||||
"ExpirationScanFrequency": 60,
|
||||
"EnableReadDeepClone": true,
|
||||
"EnableWriteDeepClone": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"imageShare": {
|
||||
"ExpireDays": 7
|
||||
},
|
||||
|
||||
|
||||
//网站根地址,为了访问文件 dicom 和上传的文档... 实测发现不用将域名拼接返回,浏览器会自动加上当前ip,避免了多环境读取环境配置文件
|
||||
//"RootUrl": "http://localhost:8060",
|
||||
|
||||
"GrpcAddress": "http://123.56.181.144:7200",
|
||||
"HostName": "123.56.181.144",
|
||||
"PortNumber": "8022",
|
||||
"UserName": "LongjunHe",
|
||||
"Password": "zhizhun2018",
|
||||
"SshHostKeyFingerprint": "",
|
||||
"GiveUpSecurityAndAcceptAnySshHostKey": true,
|
||||
"CodePrefix": "RE",
|
||||
"UserCodePrefix": "U",
|
||||
"Share": false, //
|
||||
"FileSizeLimit": 1073741824,
|
||||
"LoginExpiredTimeSpan": ":15", // Minute
|
||||
"OpenLog": true,
|
||||
"AddClinicalInfo": true
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<system.webServer>
|
||||
<security>
|
||||
<requestFiltering>
|
||||
<!--单位:字节。 -->
|
||||
<requestLimits maxAllowedContentLength="1073741824" />
|
||||
<!-- 1 GB -->
|
||||
</requestFiltering>
|
||||
</security>
|
||||
</system.webServer>
|
||||
</configuration>
|
|
@ -0,0 +1,128 @@
|
|||
<!-- HTML for static distribution bundle build -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>%(DocumentTitle)</title>
|
||||
<link href="https://fonts.googleapis.com/css?family=Open+Sans:400,700|Source+Code+Pro:300,600|Titillium+Web:400,600,700" rel="stylesheet">
|
||||
<link rel="stylesheet" type="text/css" href="./swagger-ui.css">
|
||||
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
|
||||
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
|
||||
<style>
|
||||
|
||||
html {
|
||||
box-sizing: border-box;
|
||||
overflow: -moz-scrollbars-vertical;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
background: #fafafa;
|
||||
}
|
||||
</style>
|
||||
%(HeadContent)
|
||||
</head>
|
||||
<body>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position:absolute;width:0;height:0">
|
||||
<defs>
|
||||
<symbol viewBox="0 0 20 20" id="unlocked">
|
||||
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V6h2v-.801C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8z"></path>
|
||||
</symbol>
|
||||
<symbol viewBox="0 0 20 20" id="locked">
|
||||
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8zM12 8H8V5.199C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8z" />
|
||||
</symbol>
|
||||
<symbol viewBox="0 0 20 20" id="close">
|
||||
<path d="M14.348 14.849c-.469.469-1.229.469-1.697 0L10 11.819l-2.651 3.029c-.469.469-1.229.469-1.697 0-.469-.469-.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-.469-.469-.469-1.228 0-1.697.469-.469 1.228-.469 1.697 0L10 8.183l2.651-3.031c.469-.469 1.228-.469 1.697 0 .469.469.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c.469.469.469 1.229 0 1.698z" />
|
||||
</symbol>
|
||||
<symbol viewBox="0 0 20 20" id="large-arrow">
|
||||
<path d="M13.25 10L6.109 2.58c-.268-.27-.268-.707 0-.979.268-.27.701-.27.969 0l7.83 7.908c.268.271.268.709 0 .979l-7.83 7.908c-.268.271-.701.27-.969 0-.268-.269-.268-.707 0-.979L13.25 10z" />
|
||||
</symbol>
|
||||
<symbol viewBox="0 0 20 20" id="large-arrow-down">
|
||||
<path d="M17.418 6.109c.272-.268.709-.268.979 0s.271.701 0 .969l-7.908 7.83c-.27.268-.707.268-.979 0l-7.908-7.83c-.27-.268-.27-.701 0-.969.271-.268.709-.268.979 0L10 13.25l7.418-7.141z" />
|
||||
</symbol>
|
||||
|
||||
<symbol viewBox="0 0 24 24" id="jump-to">
|
||||
<path d="M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.41L5.83 13H21V7z" />
|
||||
</symbol>
|
||||
<symbol viewBox="0 0 24 24" id="expand">
|
||||
<path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z" />
|
||||
</symbol>
|
||||
</defs>
|
||||
</svg>
|
||||
<div id="swagger-ui"></div>
|
||||
<script src="./swagger-ui-bundle.js"></script>
|
||||
<script src="./swagger-ui-standalone-preset.js"></script>
|
||||
<script src="/swagger/ui/abp.js"></script>
|
||||
<script src="/swagger/ui/abp.swagger.js?v=1"></script>
|
||||
<!--<script src="/lib/jquery/jquery.min.js"></script>-->
|
||||
<script>
|
||||
window.onload = function () {
|
||||
var configObject = JSON.parse('%(ConfigObject)');
|
||||
|
||||
// Apply mandatory parameters
|
||||
configObject.dom_id = "#swagger-ui";
|
||||
configObject.presets = [SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset];
|
||||
configObject.layout = "StandaloneLayout";
|
||||
configObject.requestInterceptor = function (request) {
|
||||
request.headers.Authorization = "Bearer " + abp.auth.getToken();
|
||||
return request;
|
||||
};
|
||||
if (!configObject.hasOwnProperty("oauth2RedirectUrl")) {
|
||||
configObject.oauth2RedirectUrl = window.location + "oauth2-redirect.html"; // use the built-in default
|
||||
}
|
||||
function getAuthorizeButtonText() {
|
||||
return abp.auth.getToken() ? 'Logout' : 'Authorize';
|
||||
}
|
||||
function getAuthorizeButtonCssClass() {
|
||||
return abp.auth.getToken() ? 'cancel' : 'authorize';
|
||||
}
|
||||
configObject.plugins = [
|
||||
function (system) {
|
||||
return {
|
||||
components: {
|
||||
authorizeBtn: function () {
|
||||
return system.React.createElement("button",
|
||||
{
|
||||
id: "authorize",
|
||||
className: "btn " + getAuthorizeButtonCssClass(),
|
||||
style: {
|
||||
lineHeight: "normal"
|
||||
},
|
||||
onClick: function () {
|
||||
var authorizeButton = document.getElementById('authorize');
|
||||
if (abp.auth.getToken()) {
|
||||
abp.swagger.logout();
|
||||
authorizeButton.innerText = getAuthorizeButtonText();
|
||||
authorizeButton.className = 'btn ' + getAuthorizeButtonCssClass();
|
||||
} else {
|
||||
abp.swagger.openAuthDialog(function () {
|
||||
authorizeButton.innerText = getAuthorizeButtonText();
|
||||
authorizeButton.className = 'btn ' + getAuthorizeButtonCssClass();
|
||||
abp.swagger.closeAuthDialog();
|
||||
});
|
||||
}
|
||||
}
|
||||
}, getAuthorizeButtonText());
|
||||
}, info: function () {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
];
|
||||
// Build a system
|
||||
SwaggerUIBundle(configObject);
|
||||
|
||||
|
||||
|
||||
}</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,117 @@
|
|||
var abp = abp || {};
|
||||
(function () {
|
||||
|
||||
/* Application paths *****************************************/
|
||||
|
||||
// Current application root path (including virtual directory if exists).
|
||||
abp.appPath = abp.appPath || '/';
|
||||
|
||||
/* AUTHORIZATION **********************************************/
|
||||
// Implements Authorization API that simplifies usage of authorization scripts generated by Abp.
|
||||
|
||||
abp.auth = abp.auth || {};
|
||||
|
||||
abp.auth.tokenCookieName = 'Abp.AuthToken';
|
||||
abp.auth.tokenHeaderName = 'Authorization';
|
||||
|
||||
abp.auth.setToken = function (authToken, expireDate) {
|
||||
abp.utils.setCookieValue(abp.auth.tokenCookieName, authToken, expireDate, abp.appPath);
|
||||
};
|
||||
|
||||
abp.auth.getToken = function () {
|
||||
return abp.utils.getCookieValue(abp.auth.tokenCookieName);
|
||||
}
|
||||
|
||||
abp.auth.clearToken = function () {
|
||||
abp.auth.setToken();
|
||||
}
|
||||
|
||||
/* UTILS ***************************************************/
|
||||
|
||||
abp.utils = abp.utils || {};
|
||||
|
||||
/**
|
||||
* Sets a cookie value for given key.
|
||||
* This is a simple implementation created to be used by ABP.
|
||||
* Please use a complete cookie library if you need.
|
||||
* @param {string} key
|
||||
* @param {string} value
|
||||
* @param {Date} expireDate (optional). If not specified the cookie will expire at the end of session.
|
||||
* @param {string} path (optional)
|
||||
*/
|
||||
abp.utils.setCookieValue = function (key, value, expireDate, path) {
|
||||
var cookieValue = encodeURIComponent(key) + '=';
|
||||
|
||||
if (value) {
|
||||
cookieValue = cookieValue + encodeURIComponent(value);
|
||||
}
|
||||
|
||||
if (expireDate) {
|
||||
cookieValue = cookieValue + "; expires=" + expireDate.toUTCString();
|
||||
}
|
||||
|
||||
if (path) {
|
||||
cookieValue = cookieValue + "; path=" + path;
|
||||
}
|
||||
|
||||
document.cookie = cookieValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets a cookie with given key.
|
||||
* This is a simple implementation created to be used by ABP.
|
||||
* Please use a complete cookie library if you need.
|
||||
* @param {string} key
|
||||
* @returns {string} Cookie value or null
|
||||
*/
|
||||
abp.utils.getCookieValue = function (key) {
|
||||
var equalities = document.cookie.split('; ');
|
||||
for (var i = 0; i < equalities.length; i++) {
|
||||
if (!equalities[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var splitted = equalities[i].split('=');
|
||||
if (splitted.length != 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (decodeURIComponent(splitted[0]) === key) {
|
||||
return decodeURIComponent(splitted[1] || '');
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Deletes cookie for given key.
|
||||
* This is a simple implementation created to be used by ABP.
|
||||
* Please use a complete cookie library if you need.
|
||||
* @param {string} key
|
||||
* @param {string} path (optional)
|
||||
*/
|
||||
abp.utils.deleteCookie = function (key, path) {
|
||||
var cookieValue = encodeURIComponent(key) + '=';
|
||||
|
||||
cookieValue = cookieValue + "; expires=" + (new Date(new Date().getTime() - 86400000)).toUTCString();
|
||||
|
||||
if (path) {
|
||||
cookieValue = cookieValue + "; path=" + path;
|
||||
}
|
||||
|
||||
document.cookie = cookieValue;
|
||||
}
|
||||
|
||||
/* SECURITY ***************************************/
|
||||
abp.security = abp.security || {};
|
||||
abp.security.antiForgery = abp.security.antiForgery || {};
|
||||
|
||||
abp.security.antiForgery.tokenCookieName = 'XSRF-TOKEN';
|
||||
abp.security.antiForgery.tokenHeaderName = 'X-XSRF-TOKEN';
|
||||
|
||||
abp.security.antiForgery.getToken = function () {
|
||||
return abp.utils.getCookieValue(abp.security.antiForgery.tokenCookieName);
|
||||
};
|
||||
|
||||
})();
|
|
@ -0,0 +1,470 @@
|
|||
var abp = abp || {};
|
||||
(function () {
|
||||
|
||||
/* md5*/
|
||||
|
||||
/*
|
||||
* A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
|
||||
* Digest Algorithm, as defined in RFC 1321.
|
||||
* Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
|
||||
* Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
|
||||
* Distributed under the BSD License
|
||||
* See http://pajhome.org.uk/crypt/md5 for more info.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Configurable variables. You may need to tweak these to be compatible with
|
||||
* the server-side, but the defaults work in most cases.
|
||||
*/
|
||||
var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
|
||||
var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */
|
||||
var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */
|
||||
|
||||
/*
|
||||
* These are the functions you'll usually want to call
|
||||
* They take string arguments and return either hex or base-64 encoded strings
|
||||
*/
|
||||
function hex_md5(s) { return binl2hex(core_md5(str2binl(s), s.length * chrsz)); }
|
||||
function b64_md5(s) { return binl2b64(core_md5(str2binl(s), s.length * chrsz)); }
|
||||
function str_md5(s) { return binl2str(core_md5(str2binl(s), s.length * chrsz)); }
|
||||
function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); }
|
||||
function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); }
|
||||
function str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); }
|
||||
|
||||
/*
|
||||
* Perform a simple self-test to see if the VM is working
|
||||
*/
|
||||
function md5_vm_test() {
|
||||
return hex_md5("abc") == "900150983cd24fb0d6963f7d28e17f72";
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the MD5 of an array of little-endian words, and a bit length
|
||||
*/
|
||||
function core_md5(x, len) {
|
||||
/* append padding */
|
||||
x[len >> 5] |= 0x80 << ((len) % 32);
|
||||
x[(((len + 64) >>> 9) << 4) + 14] = len;
|
||||
|
||||
var a = 1732584193;
|
||||
var b = -271733879;
|
||||
var c = -1732584194;
|
||||
var d = 271733878;
|
||||
|
||||
for (var i = 0; i < x.length; i += 16) {
|
||||
var olda = a;
|
||||
var oldb = b;
|
||||
var oldc = c;
|
||||
var oldd = d;
|
||||
|
||||
a = md5_ff(a, b, c, d, x[i + 0], 7, -680876936);
|
||||
d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586);
|
||||
c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819);
|
||||
b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330);
|
||||
a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897);
|
||||
d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426);
|
||||
c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341);
|
||||
b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983);
|
||||
a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416);
|
||||
d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417);
|
||||
c = md5_ff(c, d, a, b, x[i + 10], 17, -42063);
|
||||
b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162);
|
||||
a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682);
|
||||
d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101);
|
||||
c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290);
|
||||
b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329);
|
||||
|
||||
a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510);
|
||||
d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632);
|
||||
c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713);
|
||||
b = md5_gg(b, c, d, a, x[i + 0], 20, -373897302);
|
||||
a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691);
|
||||
d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083);
|
||||
c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335);
|
||||
b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848);
|
||||
a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438);
|
||||
d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690);
|
||||
c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961);
|
||||
b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501);
|
||||
a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467);
|
||||
d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784);
|
||||
c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473);
|
||||
b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734);
|
||||
|
||||
a = md5_hh(a, b, c, d, x[i + 5], 4, -378558);
|
||||
d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463);
|
||||
c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562);
|
||||
b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556);
|
||||
a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060);
|
||||
d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353);
|
||||
c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632);
|
||||
b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640);
|
||||
a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174);
|
||||
d = md5_hh(d, a, b, c, x[i + 0], 11, -358537222);
|
||||
c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979);
|
||||
b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189);
|
||||
a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487);
|
||||
d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835);
|
||||
c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520);
|
||||
b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651);
|
||||
|
||||
a = md5_ii(a, b, c, d, x[i + 0], 6, -198630844);
|
||||
d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415);
|
||||
c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905);
|
||||
b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055);
|
||||
a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571);
|
||||
d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606);
|
||||
c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523);
|
||||
b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799);
|
||||
a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359);
|
||||
d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744);
|
||||
c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380);
|
||||
b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649);
|
||||
a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070);
|
||||
d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379);
|
||||
c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259);
|
||||
b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551);
|
||||
|
||||
a = safe_add(a, olda);
|
||||
b = safe_add(b, oldb);
|
||||
c = safe_add(c, oldc);
|
||||
d = safe_add(d, oldd);
|
||||
}
|
||||
return Array(a, b, c, d);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* These functions implement the four basic operations the algorithm uses.
|
||||
*/
|
||||
function md5_cmn(q, a, b, x, s, t) {
|
||||
return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b);
|
||||
}
|
||||
function md5_ff(a, b, c, d, x, s, t) {
|
||||
return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
|
||||
}
|
||||
function md5_gg(a, b, c, d, x, s, t) {
|
||||
return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
|
||||
}
|
||||
function md5_hh(a, b, c, d, x, s, t) {
|
||||
return md5_cmn(b ^ c ^ d, a, b, x, s, t);
|
||||
}
|
||||
function md5_ii(a, b, c, d, x, s, t) {
|
||||
return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the HMAC-MD5, of a key and some data
|
||||
*/
|
||||
function core_hmac_md5(key, data) {
|
||||
var bkey = str2binl(key);
|
||||
if (bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz);
|
||||
|
||||
var ipad = Array(16), opad = Array(16);
|
||||
for (var i = 0; i < 16; i++) {
|
||||
ipad[i] = bkey[i] ^ 0x36363636;
|
||||
opad[i] = bkey[i] ^ 0x5C5C5C5C;
|
||||
}
|
||||
|
||||
var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);
|
||||
return core_md5(opad.concat(hash), 512 + 128);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add integers, wrapping at 2^32. This uses 16-bit operations internally
|
||||
* to work around bugs in some JS interpreters.
|
||||
*/
|
||||
function safe_add(x, y) {
|
||||
var lsw = (x & 0xFFFF) + (y & 0xFFFF);
|
||||
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
|
||||
return (msw << 16) | (lsw & 0xFFFF);
|
||||
}
|
||||
|
||||
/*
|
||||
* Bitwise rotate a 32-bit number to the left.
|
||||
*/
|
||||
function bit_rol(num, cnt) {
|
||||
return (num << cnt) | (num >>> (32 - cnt));
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a string to an array of little-endian words
|
||||
* If chrsz is ASCII, characters >255 have their hi-byte silently ignored.
|
||||
*/
|
||||
function str2binl(str) {
|
||||
var bin = Array();
|
||||
var mask = (1 << chrsz) - 1;
|
||||
for (var i = 0; i < str.length * chrsz; i += chrsz)
|
||||
bin[i >> 5] |= (str.charCodeAt(i / chrsz) & mask) << (i % 32);
|
||||
return bin;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert an array of little-endian words to a string
|
||||
*/
|
||||
function binl2str(bin) {
|
||||
var str = "";
|
||||
var mask = (1 << chrsz) - 1;
|
||||
for (var i = 0; i < bin.length * 32; i += chrsz)
|
||||
str += String.fromCharCode((bin[i >> 5] >>> (i % 32)) & mask);
|
||||
return str;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert an array of little-endian words to a hex string.
|
||||
*/
|
||||
function binl2hex(binarray) {
|
||||
var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
|
||||
var str = "";
|
||||
for (var i = 0; i < binarray.length * 4; i++) {
|
||||
str += hex_tab.charAt((binarray[i >> 2] >> ((i % 4) * 8 + 4)) & 0xF) +
|
||||
hex_tab.charAt((binarray[i >> 2] >> ((i % 4) * 8)) & 0xF);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert an array of little-endian words to a base-64 string
|
||||
*/
|
||||
function binl2b64(binarray) {
|
||||
var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
var str = "";
|
||||
for (var i = 0; i < binarray.length * 4; i += 3) {
|
||||
var triplet = (((binarray[i >> 2] >> 8 * (i % 4)) & 0xFF) << 16)
|
||||
| (((binarray[i + 1 >> 2] >> 8 * ((i + 1) % 4)) & 0xFF) << 8)
|
||||
| ((binarray[i + 2 >> 2] >> 8 * ((i + 2) % 4)) & 0xFF);
|
||||
for (var j = 0; j < 4; j++) {
|
||||
if (i * 8 + j * 6 > binarray.length * 32) str += b64pad;
|
||||
else str += tab.charAt((triplet >> 6 * (3 - j)) & 0x3F);
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
/* Swagger */
|
||||
|
||||
abp.swagger = abp.swagger || {};
|
||||
|
||||
abp.swagger.addAuthToken = function () {
|
||||
var authToken = abp.auth.getToken();
|
||||
if (!authToken) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var cookieAuth = new SwaggerClient.ApiKeyAuthorization(abp.auth.tokenHeaderName, 'Bearer ' + authToken, 'header');
|
||||
swaggerUi.api.clientAuthorizations.add('bearerAuth', cookieAuth);
|
||||
return true;
|
||||
}
|
||||
|
||||
abp.swagger.addCsrfToken = function () {
|
||||
var csrfToken = abp.security.antiForgery.getToken();
|
||||
if (!csrfToken) {
|
||||
return false;
|
||||
}
|
||||
var csrfCookieAuth = new SwaggerClient.ApiKeyAuthorization(abp.security.antiForgery.tokenHeaderName, csrfToken, 'header');
|
||||
swaggerUi.api.clientAuthorizations.add(abp.security.antiForgery.tokenHeaderName, csrfCookieAuth);
|
||||
return true;
|
||||
}
|
||||
|
||||
function loginUserInternal(tenantId, callback) {
|
||||
|
||||
var usernameOrEmailAddress = document.getElementById('userName').value;
|
||||
if (!usernameOrEmailAddress) {
|
||||
alert('UserName Can Not Be Null');
|
||||
return false;
|
||||
}
|
||||
|
||||
var password = document.getElementById('password').value;
|
||||
if (!password) {
|
||||
alert('PassWord Can Not Be Null');
|
||||
return false;
|
||||
}
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
|
||||
xhr.onreadystatechange = function () {
|
||||
|
||||
if (xhr.readyState === XMLHttpRequest.DONE) {
|
||||
debugger;
|
||||
if (xhr.status === 200) {
|
||||
debugger;
|
||||
|
||||
var resultdata = JSON.parse(xhr.responseText);
|
||||
if (resultdata.ErrorMessage != '') {
|
||||
alert(resultdata.ErrorMessage);
|
||||
return false;
|
||||
}
|
||||
if (resultdata.code == 300) {
|
||||
alert(resultdata.message);
|
||||
}
|
||||
else {
|
||||
var responseJSON = JSON.parse(xhr.responseText);
|
||||
var result = responseJSON;
|
||||
var expireDate = new Date(Date.now() + (60*60*24 * 1000));
|
||||
abp.auth.setToken(result.Result.JWTStr, expireDate);
|
||||
callback();
|
||||
}
|
||||
|
||||
} else {
|
||||
alert('Login failed !');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
xhr.open('POST', '/user/login', true);
|
||||
xhr.setRequestHeader('Abp.TenantId', tenantId);
|
||||
xhr.setRequestHeader('Content-type', 'application/json');
|
||||
var parm = {
|
||||
|
||||
|
||||
UserName: usernameOrEmailAddress,
|
||||
Password: hex_md5(password),
|
||||
}
|
||||
xhr.send(JSON.stringify(parm));
|
||||
//xhr.send("{" + "userName:'" + usernameOrEmailAddress + "'," + "passWord:'" + password + "'}");
|
||||
|
||||
};
|
||||
|
||||
abp.swagger.login = function (callback) {
|
||||
//Get TenantId first
|
||||
var tenancyName = document.getElementById('tenancyName').value;
|
||||
|
||||
if (tenancyName) {
|
||||
var xhrTenancyName = new XMLHttpRequest();
|
||||
xhrTenancyName.onreadystatechange = function () {
|
||||
if (xhrTenancyName.readyState === XMLHttpRequest.DONE && xhrTenancyName.status === 200) {
|
||||
var responseJSON = JSON.parse(xhrTenancyName.responseText);
|
||||
var result = responseJSON.result;
|
||||
if (result.state === 1) { // Tenant exists and active.
|
||||
loginUserInternal(result.tenantId, callback); // Login for tenant
|
||||
} else {
|
||||
alert('There is no such tenant or tenant is not active !');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
xhrTenancyName.open('POST', '/api/services/app/Account/IsTenantAvailable', true);
|
||||
xhrTenancyName.setRequestHeader('Content-type', 'application/json');
|
||||
xhrTenancyName.send("{" + "tenancyName:'" + tenancyName + "'}");
|
||||
} else {
|
||||
loginUserInternal(null, callback); // Login for host
|
||||
}
|
||||
};
|
||||
|
||||
abp.swagger.logout = function () {
|
||||
abp.auth.clearToken();
|
||||
}
|
||||
|
||||
abp.swagger.closeAuthDialog = function () {
|
||||
if (document.getElementById('abp-auth-dialog')) {
|
||||
document.getElementsByClassName("swagger-ui")[1].removeChild(document.getElementById('abp-auth-dialog'));
|
||||
}
|
||||
}
|
||||
|
||||
abp.swagger.openAuthDialog = function (loginCallback) {
|
||||
abp.swagger.closeAuthDialog();
|
||||
|
||||
var abpAuthDialog = document.createElement('div');
|
||||
abpAuthDialog.className = 'dialog-ux';
|
||||
abpAuthDialog.id = 'abp-auth-dialog';
|
||||
|
||||
document.getElementsByClassName("swagger-ui")[1].appendChild(abpAuthDialog);
|
||||
|
||||
// -- backdrop-ux
|
||||
var backdropUx = document.createElement('div');
|
||||
backdropUx.className = 'backdrop-ux';
|
||||
abpAuthDialog.appendChild(backdropUx);
|
||||
|
||||
// -- modal-ux
|
||||
var modalUx = document.createElement('div');
|
||||
modalUx.className = 'modal-ux';
|
||||
abpAuthDialog.appendChild(modalUx);
|
||||
|
||||
// -- -- modal-dialog-ux
|
||||
var modalDialogUx = document.createElement('div');
|
||||
modalDialogUx.className = 'modal-dialog-ux';
|
||||
modalUx.appendChild(modalDialogUx);
|
||||
|
||||
// -- -- -- modal-ux-inner
|
||||
var modalUxInner = document.createElement('div');
|
||||
modalUxInner.className = 'modal-ux-inner';
|
||||
modalDialogUx.appendChild(modalUxInner);
|
||||
|
||||
// -- -- -- -- modal-ux-header
|
||||
var modalUxHeader = document.createElement('div');
|
||||
modalUxHeader.className = 'modal-ux-header';
|
||||
modalUxInner.appendChild(modalUxHeader);
|
||||
|
||||
var modalHeader = document.createElement('h3');
|
||||
modalHeader.innerText = 'Authorize';
|
||||
modalUxHeader.appendChild(modalHeader);
|
||||
|
||||
// -- -- -- -- modal-ux-content
|
||||
var modalUxContent = document.createElement('div');
|
||||
modalUxContent.className = 'modal-ux-content';
|
||||
modalUxInner.appendChild(modalUxContent);
|
||||
|
||||
modalUxContent.onkeydown = function (e) {
|
||||
if (e.keyCode === 13) {
|
||||
//try to login when user presses enter on authorize modal
|
||||
abp.swagger.login(loginCallback);
|
||||
}
|
||||
};
|
||||
|
||||
//Inputs
|
||||
createInput(modalUxContent, 'tenancyName', 'Tenancy Name (Leave empty for Host)');
|
||||
createInput(modalUxContent, 'userName', 'Username or email address','text','admin');
|
||||
createInput(modalUxContent, 'password', 'Password','password');
|
||||
|
||||
//Buttons
|
||||
var authBtnWrapper = document.createElement('div');
|
||||
authBtnWrapper.className = 'auth-btn-wrapper';
|
||||
modalUxContent.appendChild(authBtnWrapper);
|
||||
|
||||
//Close button
|
||||
var closeButton = document.createElement('button');
|
||||
closeButton.className = 'btn modal-btn auth btn-done button';
|
||||
closeButton.innerText = 'Close';
|
||||
closeButton.style.marginRight = '5px';
|
||||
closeButton.onclick = abp.swagger.closeAuthDialog;
|
||||
authBtnWrapper.appendChild(closeButton);
|
||||
|
||||
//Authorize button
|
||||
var authorizeButton = document.createElement('button');
|
||||
authorizeButton.className = 'btn modal-btn auth authorize button';
|
||||
authorizeButton.innerText = 'Login';
|
||||
authorizeButton.onclick = function () {
|
||||
abp.swagger.login(loginCallback);
|
||||
};
|
||||
authBtnWrapper.appendChild(authorizeButton);
|
||||
}
|
||||
|
||||
function createInput(container, id, title, type, value="") {
|
||||
var wrapper = document.createElement('div');
|
||||
wrapper.className = 'wrapper';
|
||||
if (id == "tenancyName") {
|
||||
wrapper.style.display = 'none';
|
||||
}
|
||||
container.appendChild(wrapper);
|
||||
|
||||
var label = document.createElement('label');
|
||||
label.innerText = title;
|
||||
wrapper.appendChild(label);
|
||||
|
||||
var section = document.createElement('section');
|
||||
section.className = 'block-tablet col-10-tablet block-desktop col-10-desktop';
|
||||
wrapper.appendChild(section);
|
||||
|
||||
var input = document.createElement('input');
|
||||
input.id = id;
|
||||
input.type = type ? type : 'text';
|
||||
input.style.width = '100%';
|
||||
input.value = value;
|
||||
input.autocomplete = "off";
|
||||
|
||||
|
||||
section.appendChild(input);
|
||||
}
|
||||
|
||||
})();
|
|
@ -0,0 +1,97 @@
|
|||
using Castle.DynamicProxy;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace IRaCIS.Core.Application.AOP
|
||||
{
|
||||
public abstract class AsyncInterceptorBase : IInterceptor
|
||||
{
|
||||
public AsyncInterceptorBase()
|
||||
{
|
||||
}
|
||||
|
||||
public void Intercept(IInvocation invocation)
|
||||
{
|
||||
BeforeProceed(invocation);
|
||||
invocation.Proceed();
|
||||
if (IsAsyncMethod(invocation.MethodInvocationTarget))
|
||||
{
|
||||
invocation.ReturnValue = InterceptAsync((dynamic)invocation.ReturnValue, invocation);
|
||||
}
|
||||
else
|
||||
{
|
||||
AfterProceedSync(invocation);
|
||||
}
|
||||
}
|
||||
|
||||
private bool CheckMethodReturnTypeIsTaskType(MethodInfo method)
|
||||
{
|
||||
var methodReturnType = method.ReturnType;
|
||||
if (methodReturnType.IsGenericType)
|
||||
{
|
||||
if (methodReturnType.GetGenericTypeDefinition() == typeof(Task<>) ||
|
||||
methodReturnType.GetGenericTypeDefinition() == typeof(ValueTask<>))
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (methodReturnType == typeof(Task) ||
|
||||
methodReturnType == typeof(ValueTask))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool IsAsyncMethod(MethodInfo method)
|
||||
{
|
||||
bool isDefAsync = Attribute.IsDefined(method, typeof(AsyncStateMachineAttribute), false);
|
||||
bool isTaskType = CheckMethodReturnTypeIsTaskType(method);
|
||||
bool isAsync = isDefAsync && isTaskType;
|
||||
|
||||
return isAsync;
|
||||
}
|
||||
|
||||
protected object ProceedAsyncResult { get; set; }
|
||||
|
||||
|
||||
private async Task InterceptAsync(Task task, IInvocation invocation)
|
||||
{
|
||||
await task.ConfigureAwait(false);
|
||||
await AfterProceedAsync(invocation, false);
|
||||
}
|
||||
|
||||
private async Task<TResult> InterceptAsync<TResult>(Task<TResult> task, IInvocation invocation)
|
||||
{
|
||||
ProceedAsyncResult = await task.ConfigureAwait(false);
|
||||
await AfterProceedAsync(invocation, true);
|
||||
return (TResult)ProceedAsyncResult;
|
||||
}
|
||||
|
||||
private async ValueTask InterceptAsync(ValueTask task, IInvocation invocation)
|
||||
{
|
||||
await task.ConfigureAwait(false);
|
||||
await AfterProceedAsync(invocation, false);
|
||||
}
|
||||
|
||||
private async ValueTask<TResult> InterceptAsync<TResult>(ValueTask<TResult> task, IInvocation invocation)
|
||||
{
|
||||
ProceedAsyncResult = await task.ConfigureAwait(false);
|
||||
await AfterProceedAsync(invocation, true);
|
||||
return (TResult)ProceedAsyncResult;
|
||||
}
|
||||
|
||||
protected virtual void BeforeProceed(IInvocation invocation) { }
|
||||
|
||||
protected virtual void AfterProceedSync(IInvocation invocation) { }
|
||||
|
||||
protected virtual Task AfterProceedAsync(IInvocation invocation, bool hasAsynResult)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,499 @@
|
|||
//using System;
|
||||
//using Castle.DynamicProxy;
|
||||
//using IRaCIS.Core.Application.Contracts.Dicom.DTO;
|
||||
//using IRaCIS.Core.Infra.EFCore;
|
||||
|
||||
//using System.Linq;
|
||||
//using IRaCIS.Core.Domain.Models;
|
||||
//using IRaCIS.Core.Domain.Share;
|
||||
|
||||
//namespace IRaCIS.Core.API.Utility.AOP
|
||||
//{
|
||||
//#pragma warning disable
|
||||
// public class QANoticeAOP : IInterceptor
|
||||
// {
|
||||
// private readonly IRepository<QANotice> _qaNoticeRepository;
|
||||
|
||||
// private readonly IRepository<DicomStudy> _studyRepository;
|
||||
// private readonly IRepository<TrialUser> _userTrialRepository;
|
||||
// private readonly IRepository<TrialSiteUser> _userTrialSiteRepository;
|
||||
// private readonly IUserInfo _userInfo;
|
||||
|
||||
// public QANoticeAOP(IRepository<QANotice> qaNoticeRepository,
|
||||
// IUserInfo userInfo, IRepository<DicomStudy> studyRepository, IRepository<TrialUser> userTrialRepository, IRepository<TrialSiteUser> userTrialSiteRepository)
|
||||
// {
|
||||
// _qaNoticeRepository = qaNoticeRepository;
|
||||
|
||||
// _studyRepository = studyRepository;
|
||||
// _userTrialRepository = userTrialRepository;
|
||||
// _userTrialSiteRepository = userTrialSiteRepository;
|
||||
// _userInfo = userInfo;
|
||||
// }
|
||||
|
||||
// public void Intercept(IInvocation invocation)
|
||||
// {
|
||||
// //处理拦截的方法
|
||||
// invocation.Proceed();
|
||||
|
||||
// if (invocation.Method.Name == "UpdateStudyStatus")
|
||||
// {
|
||||
// var studyStatus = invocation.Arguments[0] as StudyStatusDetailCommand;
|
||||
|
||||
// var study = _studyRepository.FirstOrDefault(t=>t.Id==studyStatus.StudyId);
|
||||
|
||||
|
||||
// if (study.Status == (int)StudyStatus.Uploaded)
|
||||
// {
|
||||
// _qaNoticeRepository.Add(new QANotice()
|
||||
// {
|
||||
// TrialId = study.TrialId,
|
||||
// SubjectVisitId = study.Id,
|
||||
// FromUser = _userInfo.RealName,
|
||||
// FromUserId = _userInfo.Id,
|
||||
// FromUserType = _userInfo.UserTypeShortName,
|
||||
|
||||
// NoticeTypeEnum = NoticeType.NotNeedNotice,
|
||||
// NeedDeal = false,
|
||||
// StudyStatusStr = "Uploaded",
|
||||
// Message = $"CRC : {_userInfo.RealName} has uploaded {study.StudyCode} ",
|
||||
// SendTime = DateTime.Now,
|
||||
// });
|
||||
// }
|
||||
|
||||
// #region 处理QA通知模块
|
||||
|
||||
// //查询项目的参与者 和 负责site下CRC用户
|
||||
// var trialUserList = _userTrialRepository.Where(t => t.TrialId == study.TrialId).ToList();
|
||||
|
||||
// // 找到该study 关联Site 下的CRC
|
||||
|
||||
// var crcList = _userTrialSiteRepository.Where(t =>
|
||||
// t.SiteId == study.SiteId && t.User.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator && t.TrialId == study.TrialId).ToList();
|
||||
|
||||
|
||||
// var qaList = trialUserList.Where(t => t.User.UserTypeEnum == UserTypeEnum.IQC).ToList();
|
||||
|
||||
// var pm = trialUserList.FirstOrDefault(t => t.User.UserTypeEnum == UserTypeEnum.ProjectManager);
|
||||
|
||||
|
||||
|
||||
|
||||
// // CRC =>QA
|
||||
// if (studyStatus.Status == (int)StudyStatus.QARequested)
|
||||
// {
|
||||
// //找出当前操作的CRC
|
||||
// //PM 或者admin可以代替CRC角色 不能从CRC列表中查询用户
|
||||
// //var currentCRC = trialUserList.First(t => t.UserId == _userInfo.Id);
|
||||
|
||||
// var notice = new QANotice()
|
||||
// {
|
||||
// TrialId = study.TrialId,
|
||||
// SubjectVisitId = study.Id,
|
||||
// FromUser = _userInfo.RealName,
|
||||
// FromUserId = _userInfo.Id,
|
||||
// FromUserType = _userInfo.UserTypeShortName,
|
||||
|
||||
// //FromUser = currentCRC.UserRealName,
|
||||
// //FromUserId = _userInfo.Id,
|
||||
// //FromUserType = currentCRC.UserType,
|
||||
// NoticeTypeEnum = NoticeType.CRC_RequestToQA_NoticeQA,
|
||||
// NeedDeal = true,
|
||||
// StudyStatusStr = "QA Requested",
|
||||
// Message =
|
||||
// $"CRC -> QA : {_userInfo.RealName} request QA {study.StudyCode} , Inquiry can be performed! ",
|
||||
// SendTime = DateTime.Now,
|
||||
// };
|
||||
|
||||
// qaList.ForEach(t => notice.QANoticeUserList.Add(new QANoticeUser()
|
||||
// {
|
||||
// QANoticeId = notice.Id,
|
||||
// SubjectVisitId = study.Id,
|
||||
// ToUser = t.User.LastName + " / " + t.User.FirstName,
|
||||
// ToUserId = t.UserId,
|
||||
// ToUserType = t.User.UserTypeRole.UserTypeShortName
|
||||
// }));
|
||||
|
||||
// _qaNoticeRepository.Add(notice);
|
||||
|
||||
// //DealRequestToQA(study.Id);
|
||||
|
||||
// var needDealNoticeList = _qaNoticeRepository.AsQueryable()
|
||||
// .Where(t => t.SubjectVisitId == study.Id && t.NeedDeal && t.NoticeTypeEnum == NoticeType.CRC_RequestToQA_NoticeQA).ToList();
|
||||
|
||||
// needDealNoticeList.ForEach(t =>
|
||||
// {
|
||||
// t.NeedDeal = false;
|
||||
// t.DealTime = DateTime.Now;
|
||||
// _qaNoticeRepository.Update(t);
|
||||
// });
|
||||
// }
|
||||
|
||||
// // QA =>CRC 向CRC推送消息影像有问题 同时作为 requestToQA 的边界
|
||||
// else if (studyStatus.Status == (int)StudyStatus.QAing)
|
||||
// {
|
||||
// //找出当前操作的QA 如果是pm 或者admin 代替操作 此时会有问题 所以 谁代替,就以谁的名义执行
|
||||
// //var currentQA = qaList.First(t => t.UserId == _userInfo.Id);
|
||||
// //var currentQA = trialUserList.First(t => t.UserId == _userInfo.Id);
|
||||
|
||||
// //在项目CRC列表中筛选出 负责该study关联 site的CRC
|
||||
// var siteCRCList = _userTrialSiteRepository.Where(t =>
|
||||
// t.SiteId == study.SiteId && t.User.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator && t.TrialId == study.TrialId).ToList();
|
||||
|
||||
// //查询项目的参与者 和 负责site下CRC用户
|
||||
|
||||
|
||||
|
||||
// var notice = new QANotice()
|
||||
// {
|
||||
// TrialId = study.TrialId,
|
||||
// SubjectVisitId = study.Id,
|
||||
// //FromUser = currentQA.UserRealName,
|
||||
// //FromUserId = _userInfo.Id,
|
||||
// //FromUserType = currentQA.UserType,
|
||||
// FromUser = _userInfo.RealName,
|
||||
// FromUserId = _userInfo.Id,
|
||||
// FromUserType = _userInfo.UserTypeShortName,
|
||||
// NoticeTypeEnum = NoticeType.QA_InQA_NoticeCRC,
|
||||
// NeedDeal = true,
|
||||
// StudyStatusStr = "In QA",
|
||||
// Message = $"QA -> CRC : {_userInfo.RealName} inquiry {study.StudyCode} ",
|
||||
// SendTime = DateTime.Now,
|
||||
// };
|
||||
|
||||
// siteCRCList.ForEach(t => notice.QANoticeUserList.Add(new QANoticeUser()
|
||||
// {
|
||||
// QANoticeId = notice.Id,
|
||||
// SubjectVisitId = study.Id,
|
||||
// ToUser = t.User.LastName + " / " + t.User.FirstName,
|
||||
// ToUserId = t.UserId,
|
||||
// ToUserType = t.UserTypeRole.UserTypeShortName
|
||||
// }));
|
||||
|
||||
// //添加 发送给CRC的消息 消息和CRC是 一对多
|
||||
// _qaNoticeRepository.Add(notice);
|
||||
|
||||
|
||||
// //处理 消息 标记已处理
|
||||
// var needDealNoticeList = _qaNoticeRepository.AsQueryable()
|
||||
// .Where(t => t.SubjectVisitId == study.Id && t.NeedDeal &&
|
||||
// (t.NoticeTypeEnum == NoticeType.CRC_RequestToQA_NoticeQA ||
|
||||
// t.NoticeTypeEnum == NoticeType.CRC_ReUpload_NoticeQA ||
|
||||
// t.NoticeTypeEnum == NoticeType.CRC_QARecordDialogPost_NoticeQA)).ToList();
|
||||
|
||||
// needDealNoticeList.ForEach(t =>
|
||||
// {
|
||||
// t.NeedDeal = false;
|
||||
// t.DealTime = DateTime.Now;
|
||||
// _qaNoticeRepository.Update(t);
|
||||
// });
|
||||
|
||||
|
||||
// }
|
||||
// // QA =>QA 给自己的消息 通知需要匿名化 同时作为 requestToQA 的边界
|
||||
// else if (studyStatus.Status == (int)StudyStatus.QAFinish)
|
||||
// {
|
||||
|
||||
// //找出当前操作的QA 如果是pm 或者admin 代替操作 此时会有问题 所以 谁代替,就以谁的名义执行
|
||||
// //var currentQA = qaList.First(t => t.UserId == _userInfo.Id);
|
||||
// //var currentQA = trialUserList.First(t => t.UserId == _userInfo.Id);
|
||||
|
||||
// //发送给当前项目QA列表
|
||||
|
||||
// var notice = new QANotice()
|
||||
// {
|
||||
// TrialId = study.TrialId,
|
||||
// SubjectVisitId = study.Id,
|
||||
// //FromUser = currentQA.UserRealName,
|
||||
// //FromUserId = _userInfo.Id,
|
||||
// //FromUserType = currentQA.UserType,
|
||||
// FromUser = _userInfo.RealName,
|
||||
// FromUserId = _userInfo.Id,
|
||||
// FromUserType = _userInfo.UserTypeShortName,
|
||||
// NoticeTypeEnum = NoticeType.QA_QAPass_NoticeQA,
|
||||
// NeedDeal = true,
|
||||
// StudyStatusStr = "QA-Passed",
|
||||
// Message =
|
||||
// $"QA -> QA : {_userInfo.RealName} inquiry {study.StudyCode} finished,Anonymization can be performed!",
|
||||
// SendTime = DateTime.Now,
|
||||
// };
|
||||
|
||||
// qaList.ForEach(t => notice.QANoticeUserList.Add(new QANoticeUser()
|
||||
// {
|
||||
// QANoticeId = notice.Id,
|
||||
// SubjectVisitId = study.Id,
|
||||
// ToUser = t.User.LastName+" / "+t.User.FirstName,
|
||||
// ToUserId = t.UserId,
|
||||
// ToUserType = t.User.UserTypeRole.UserTypeShortName
|
||||
// }));
|
||||
|
||||
// _qaNoticeRepository.Add(notice);
|
||||
|
||||
// //处理 消息 标记已处理 存在意外情况 qa发给CRC的 但是qa里面设置了 通过或者不通过 此时qa发送的消息也设置为已处理
|
||||
// var needDealNoticeList = _qaNoticeRepository.AsQueryable()
|
||||
// .Where(t => t.SubjectVisitId == study.Id && t.NeedDeal &&
|
||||
// (t.NoticeTypeEnum == NoticeType.CRC_RequestToQA_NoticeQA ||
|
||||
// t.NoticeTypeEnum == NoticeType.CRC_ReUpload_NoticeQA ||
|
||||
// t.NoticeTypeEnum == NoticeType.CRC_QARecordDialogPost_NoticeQA ||
|
||||
|
||||
// t.NoticeTypeEnum == NoticeType.QA_QARecordDialogPost_NoticeCRC ||
|
||||
// t.NoticeTypeEnum == NoticeType.QA_InQA_NoticeCRC ||
|
||||
// t.NoticeTypeEnum == NoticeType.QA_AddQARecord_NoticeCRC)).ToList();
|
||||
|
||||
// needDealNoticeList.ForEach(t =>
|
||||
// {
|
||||
// t.NeedDeal = false;
|
||||
// t.DealTime = DateTime.Now;
|
||||
// _qaNoticeRepository.Update(t);
|
||||
// });
|
||||
|
||||
|
||||
// }
|
||||
// // QA =>CRC 暂时不用发送消息给CRC 因为CRC 暂时没有入口回复 同时作为 requestToQA 的边界
|
||||
// else if (studyStatus.Status == (int)StudyStatus.QAFInishNotPass)
|
||||
// {
|
||||
|
||||
// _qaNoticeRepository.Add(new QANotice()
|
||||
// {
|
||||
// TrialId = study.TrialId,
|
||||
// SubjectVisitId = study.Id,
|
||||
// FromUser = _userInfo.RealName,
|
||||
// FromUserId = _userInfo.Id,
|
||||
// FromUserType = _userInfo.UserTypeShortName,
|
||||
// NoticeTypeEnum = NoticeType.NotNeedNotice,
|
||||
// NeedDeal = false,
|
||||
// StudyStatusStr = "QA-Failed",
|
||||
// Message = $"QA : {_userInfo.RealName} set {study.StudyCode} QA-Failed !",
|
||||
// SendTime = DateTime.Now,
|
||||
// });
|
||||
|
||||
// //处理 消息 标记已处理
|
||||
// var needDealNoticeList = _qaNoticeRepository.AsQueryable()
|
||||
// .Where(t => t.SubjectVisitId == study.Id && t.NeedDeal &&
|
||||
// (t.NoticeTypeEnum == NoticeType.CRC_RequestToQA_NoticeQA ||
|
||||
// t.NoticeTypeEnum == NoticeType.CRC_ReUpload_NoticeQA ||
|
||||
// t.NoticeTypeEnum == NoticeType.CRC_QARecordDialogPost_NoticeQA ||
|
||||
|
||||
|
||||
// t.NoticeTypeEnum == NoticeType.QA_QARecordDialogPost_NoticeCRC ||
|
||||
// t.NoticeTypeEnum == NoticeType.QA_InQA_NoticeCRC ||
|
||||
// t.NoticeTypeEnum == NoticeType.QA_AddQARecord_NoticeCRC)).ToList();
|
||||
|
||||
// needDealNoticeList.ForEach(t =>
|
||||
// {
|
||||
// t.NeedDeal = false;
|
||||
// t.DealTime = DateTime.Now;
|
||||
// _qaNoticeRepository.Update(t);
|
||||
// });
|
||||
// }
|
||||
|
||||
// #endregion
|
||||
|
||||
|
||||
// }
|
||||
|
||||
// else if (invocation.Method.Name == "ReUploadSameStudy")
|
||||
// {
|
||||
// var studyId = Guid.Parse(invocation.Arguments[0].ToString());
|
||||
|
||||
// var study = _studyRepository.FirstOrDefault(t => t.Id == studyId);
|
||||
// var status = study.Status;
|
||||
|
||||
// //处理CRC 重传时 QA消息
|
||||
|
||||
// if (status == (int)StudyStatus.QAing)
|
||||
// {
|
||||
// //查询项目的参与者 和 负责site下CRC用户
|
||||
// var trialUserList = _userTrialRepository.Where(t => t.TrialId == study.TrialId).ToList();
|
||||
|
||||
// // 找到该study 关联Site 下的CRC
|
||||
|
||||
// var crcList = _userTrialSiteRepository.Where(t =>
|
||||
// t.SiteId == study.SiteId && t.User.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator && t.TrialId == study.TrialId).ToList();
|
||||
|
||||
// var qaList = trialUserList.Where(t => t.User.UserTypeEnum == UserTypeEnum.IQC).ToList();
|
||||
|
||||
// //CRC =>QA CRC的职能被PM 或者admin代替
|
||||
// //if (_userInfo.UserTypeEnumInt == (int)UserType.ClinicalResearchCoordinator)
|
||||
// {
|
||||
// //PM 或者admin可以代替CRC角色 不能从CRC列表中查询用户
|
||||
// //var currentCRC = trialUserList.First(t => t.UserId == _userInfo.Id);
|
||||
|
||||
// var notice = new QANotice()
|
||||
// {
|
||||
// TrialId = study.TrialId,
|
||||
// SubjectVisitId = study.Id,
|
||||
// //FromUser = currentCRC.UserRealName,
|
||||
// //FromUserId = _userInfo.Id,
|
||||
// //FromUserType = currentCRC.UserType,
|
||||
// FromUser = _userInfo.RealName,
|
||||
// FromUserId = _userInfo.Id,
|
||||
// FromUserType = _userInfo.UserTypeShortName,
|
||||
// NoticeTypeEnum = NoticeType.CRC_ReUpload_NoticeQA,
|
||||
// NeedDeal = true,
|
||||
// Message = $"CRC -> QA :{_userInfo.RealName} has reuploaded {study.StudyCode} , Need to be inquiry again",
|
||||
// SendTime = DateTime.Now
|
||||
// };
|
||||
|
||||
// qaList.ForEach(t => notice.QANoticeUserList.Add(new QANoticeUser()
|
||||
// {
|
||||
// QANoticeId = notice.Id,
|
||||
// SubjectVisitId = study.Id,
|
||||
// ToUser = t.User.LastName+" / "+t.User.FirstName,
|
||||
// ToUserId = t.UserId,
|
||||
// ToUserType = t.User.UserTypeRole.UserTypeShortName
|
||||
// }));
|
||||
|
||||
// _qaNoticeRepository.Add(notice);
|
||||
|
||||
// //这里作为 QA 设置 Inqa 状态的回复 或者QA和CRC对话的
|
||||
// var needDealNoticeList = _qaNoticeRepository.Where(t => t.SubjectVisitId == study.Id && t.NeedDeal
|
||||
// && (t.NoticeTypeEnum == NoticeType.QA_InQA_NoticeCRC || t.NoticeTypeEnum == NoticeType.QA_QARecordDialogPost_NoticeCRC))
|
||||
// .ToList();
|
||||
|
||||
// needDealNoticeList.ForEach(t =>
|
||||
// {
|
||||
// t.NeedDeal = false;
|
||||
// t.DealTime = DateTime.Now;
|
||||
// _qaNoticeRepository.Update(t);
|
||||
// });
|
||||
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// //不是QAing 的重传 不发送qa消息
|
||||
// return;
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// }
|
||||
|
||||
// else if (invocation.Method.Name == "DicomAnonymize")
|
||||
// {
|
||||
// var studyId = Guid.Parse(invocation.Arguments[0].ToString());
|
||||
|
||||
// var study = _studyRepository.FirstOrDefault(t => t.Id == studyId);
|
||||
|
||||
// #region 处理QA通知 匿名化完毕 通知PM
|
||||
|
||||
// //查询项目的参与者 和 负责site下CRC用户
|
||||
// var trialUserList = _userTrialRepository.Where(t => t.TrialId == study.TrialId).ToList();
|
||||
|
||||
// // 找到该study 关联Site 下的CRC
|
||||
|
||||
// var crcList = _userTrialSiteRepository.Where(t =>
|
||||
// t.SiteId == study.SiteId && t.User.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator && t.TrialId == study.TrialId).ToList();
|
||||
|
||||
// var qaList = trialUserList.Where(t => t.User.UserTypeEnum == UserTypeEnum.IQC).ToList();
|
||||
|
||||
// //
|
||||
// var pm = trialUserList.FirstOrDefault(t => t.User.UserTypeEnum == UserTypeEnum.ProjectManager);
|
||||
|
||||
|
||||
// //找出当前操作的QA 如果是pm 或者admin 代替操作 此时会有问题 所以 谁代替,就以谁的名义执行
|
||||
// //var currentQA = trialUserList.First(t =>
|
||||
// // t.UserTypeEnum == UserType.IQC && t.UserId == _userInfo.Id);
|
||||
// //var currentQA = trialUserList.First(t => t.UserId == _userInfo.Id);
|
||||
|
||||
// var notice = new QANotice()
|
||||
// {
|
||||
// TrialId = study.TrialId,
|
||||
// SubjectVisitId = study.Id,
|
||||
// //FromUser = currentQA.UserRealName,
|
||||
// //FromUserId = _userInfo.Id,
|
||||
// //FromUserType = currentQA.UserType,
|
||||
// FromUser = _userInfo.RealName,
|
||||
// FromUserId = _userInfo.Id,
|
||||
// FromUserType = _userInfo.UserTypeShortName,
|
||||
// NoticeTypeEnum = NoticeType.QA_Anonymized_NoticeQA,
|
||||
// NeedDeal = true,
|
||||
// StudyStatusStr = "Anonymized",
|
||||
// //Message = $"QA -> PM :{_userInfo.RealName} has anonymized {study.StudyCode} ,Forward can be performed!!",
|
||||
// Message = $"QA -> QA :{_userInfo.RealName} has anonymized {study.StudyCode} ,Forward can be performed!!",
|
||||
|
||||
// SendTime = DateTime.Now,
|
||||
|
||||
// };
|
||||
|
||||
// //notice.QANoticeUserList.Add(new QANoticeUser()
|
||||
// //{
|
||||
// // QANoticeId = notice.Id,
|
||||
// // StudyId = study.Id,
|
||||
// // ToUser = pm.UserRealName,
|
||||
// // ToUserId = pm.UserId,
|
||||
// // ToUserType = pm.UserType
|
||||
// //});
|
||||
|
||||
// qaList.ForEach(t => notice.QANoticeUserList.Add(new QANoticeUser()
|
||||
// {
|
||||
// QANoticeId = notice.Id,
|
||||
// SubjectVisitId = study.Id,
|
||||
// ToUser = t.User.LastName+" / "+t.User.FirstName,
|
||||
// ToUserId = t.UserId,
|
||||
// ToUserType = t.User.UserTypeRole.UserTypeShortName
|
||||
// }));
|
||||
|
||||
// _qaNoticeRepository.Add(notice);
|
||||
|
||||
// var needDealNoticeList = _qaNoticeRepository.AsQueryable()
|
||||
// .Where(t => t.SubjectVisitId == study.Id && t.NeedDeal && (t.NoticeTypeEnum == NoticeType.QA_QAPass_NoticeQA)).ToList();
|
||||
|
||||
// needDealNoticeList.ForEach(t =>
|
||||
// {
|
||||
// t.NeedDeal = false;
|
||||
// t.DealTime = DateTime.Now;
|
||||
// _qaNoticeRepository.Update(t);
|
||||
// });
|
||||
|
||||
|
||||
// #endregion
|
||||
// }
|
||||
|
||||
// else if (invocation.Method.Name == "ForwardStudy")
|
||||
// {
|
||||
// var studyId = Guid.Parse(invocation.Arguments[0].ToString());
|
||||
|
||||
// var study = _studyRepository.FirstOrDefault(t => t.Id == studyId);
|
||||
|
||||
// //匿名化操作产生的消息 设置为已经处理
|
||||
// _qaNoticeRepository.Add(new QANotice()
|
||||
// {
|
||||
// TrialId = study.TrialId,
|
||||
// SubjectVisitId = study.Id,
|
||||
// //FromUser = currentQA.UserRealName,
|
||||
// //FromUserId = _userInfo.Id,
|
||||
// //FromUserType = currentQA.UserType,
|
||||
// FromUser = _userInfo.RealName,
|
||||
// FromUserId = _userInfo.Id,
|
||||
// FromUserType = _userInfo.UserTypeShortName,
|
||||
// NoticeTypeEnum = NoticeType.NotNeedNotice,
|
||||
// NeedDeal = false,
|
||||
// StudyStatusStr = "Forwarded",
|
||||
// //Message = $"PM :{_userInfo.RealName} has forwarded {study.StudyCode} !",
|
||||
// Message = $"QA :{_userInfo.RealName} has forwarded {study.StudyCode} !",
|
||||
// SendTime = DateTime.Now,
|
||||
// });
|
||||
|
||||
// var needDealList = _qaNoticeRepository.Where(t =>
|
||||
// t.SubjectVisitId == study.Id && t.NeedDeal && t.NoticeTypeEnum == NoticeType.QA_Anonymized_NoticeQA).ToList();
|
||||
|
||||
// needDealList.ForEach(t =>
|
||||
// {
|
||||
// t.NeedDeal = false;
|
||||
// t.DealTime = DateTime.Now;
|
||||
// _qaNoticeRepository.Update(t);
|
||||
// });
|
||||
// }
|
||||
|
||||
// var success = _qaNoticeRepository.SaveChanges();
|
||||
|
||||
// if (!success)
|
||||
// {
|
||||
// throw new Exception("Send QA message failed");
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
// }
|
||||
//}
|
|
@ -0,0 +1,89 @@
|
|||
using Castle.DynamicProxy;
|
||||
using EasyCaching.Core;
|
||||
using IRaCIS.Application.Contracts;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
|
||||
namespace IRaCIS.Core.Application.AOP
|
||||
{
|
||||
public class TrialStatusAutofacAOP : IAsyncInterceptor
|
||||
{
|
||||
private readonly IEasyCachingProvider _provider;
|
||||
|
||||
public TrialStatusAutofacAOP(IEasyCachingProvider provider)
|
||||
{
|
||||
_provider = provider;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void InterceptAsynchronous(IInvocation invocation)
|
||||
{
|
||||
invocation.Proceed();
|
||||
}
|
||||
|
||||
//这里AOP 处理两个方法 分别是 项目的添加和更新、项目状态的变更
|
||||
|
||||
public void InterceptAsynchronous<TResult>(IInvocation invocation)
|
||||
{
|
||||
|
||||
|
||||
//处理拦截的方法
|
||||
invocation.Proceed();
|
||||
|
||||
|
||||
|
||||
dynamic result = invocation.ReturnValue;
|
||||
|
||||
//接口成功了,才修改缓存
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#region 处理项目列表的查询 在前端界面已经在某个界面,但是服务器重置了,此时没有缓存项目信息,接口不能正确返回,因故采用,启动时查询,每天查询一次,缓存一天,然后项目添加、更改状态时,及时更新
|
||||
|
||||
//if (invocation.Method.Name == "GetTrialList")
|
||||
//{
|
||||
// //在此 将当前查询的项目Id 和对应的项目状态进行缓存
|
||||
// dynamic result = invocation.ReturnValue;
|
||||
// foreach (var item in result.CurrentPageData)
|
||||
// {
|
||||
// _provider.Remove(item.Id.ToString());
|
||||
// _provider.Set(item.Id.ToString(), item.TrialStatusStr.ToString(), TimeSpan.FromDays(1));
|
||||
// }
|
||||
//}
|
||||
|
||||
#endregion
|
||||
|
||||
if (invocation.Method.Name == "AddOrUpdateTrial")
|
||||
{
|
||||
//如果是添加 那么将对应的初始状态加进去 更新状态是单独操作的
|
||||
|
||||
var trialModel = (invocation.Arguments[0] as TrialCommand).IfNullThrowConvertException();
|
||||
if (trialModel.Id == null || trialModel.Id == Guid.Empty)
|
||||
{
|
||||
_provider.Set(result.Data.Id.ToString(), StaticData.TrialOngoing, TimeSpan.FromDays(1));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
// 更新缓存
|
||||
else if (invocation.Method.Name == "UpdateTrialStatus")
|
||||
{
|
||||
//项目状态更新,也需要及时更新
|
||||
_provider.Set(invocation.Arguments[0].ToString(), invocation.Arguments[1].ToString(), TimeSpan.FromDays(1));
|
||||
|
||||
////Test参数是否符合要求
|
||||
//var tt = invocation.Arguments[0].ToString();
|
||||
//var cc = _provider.Get<string>(invocation.Arguments[0].ToString());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void InterceptSynchronous(IInvocation invocation)
|
||||
{
|
||||
invocation.Proceed();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
using Castle.DynamicProxy;
|
||||
using IRaCIS.Application.Services;
|
||||
using IRaCIS.Application.Contracts;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
|
||||
namespace IRaCIS.Core.API.Utility.AOP
|
||||
{
|
||||
/// <summary>
|
||||
///服务动态生成api AOP 此时会失效
|
||||
/// </summary>
|
||||
public class UserAddAOP : IInterceptor
|
||||
{
|
||||
private readonly IMailVerificationService _mailVerificationService;
|
||||
private readonly ILogger<UserAddAOP> _logger;
|
||||
|
||||
public UserAddAOP(IMailVerificationService mailVerificationService, ILogger<UserAddAOP> logger)
|
||||
{
|
||||
_mailVerificationService = mailVerificationService;
|
||||
_logger = logger;
|
||||
}
|
||||
public void Intercept(IInvocation invocation)
|
||||
{
|
||||
|
||||
var userInfo = (invocation.Arguments[0] as UserCommand).IfNullThrowConvertException();
|
||||
|
||||
|
||||
//处理拦截的方法
|
||||
invocation.Proceed();
|
||||
|
||||
//在此 发送邮件
|
||||
dynamic result = invocation.ReturnValue;
|
||||
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
var userId = result.Data.Id;
|
||||
var verificationCode = result.Data.VerificationCode;
|
||||
|
||||
|
||||
_logger.LogInformation($"Sent to {userInfo.UserName} email {userInfo.EMail} init password {verificationCode}");
|
||||
|
||||
_mailVerificationService.SendMail(userId, userInfo.UserName, userInfo.EMail, verificationCode).GetAwaiter().GetResult();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
using IRaCIS.Application.Contracts;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
|
||||
namespace IRaCIS.Core.Application.Auth
|
||||
{
|
||||
public class IRaCISClaims
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string FullName { get; set; } = String.Empty;
|
||||
public string Code { get; set; } = String.Empty;
|
||||
public string RealName { get; set; } = String.Empty;
|
||||
|
||||
public string UserTypeShortName { get; set; } = String.Empty;
|
||||
|
||||
public UserTypeEnum UserTypeEnum { get; set; }
|
||||
|
||||
public string PermissionStr { get; set; } = String.Empty;
|
||||
|
||||
public Guid UserTypeId { get; set; }
|
||||
|
||||
public int IsAdmin { get; }
|
||||
|
||||
public string Phone { get; set; } = String.Empty;
|
||||
|
||||
public static IRaCISClaims Create(UserBasicInfo user)
|
||||
{
|
||||
return new IRaCISClaims
|
||||
{
|
||||
Id = user.Id,
|
||||
FullName = user.UserName,
|
||||
RealName = user.RealName,
|
||||
UserTypeEnum=user.UserTypeEnum,
|
||||
UserTypeId=user.UserTypeId,
|
||||
Code = user.Code,
|
||||
PermissionStr = user.PermissionStr,
|
||||
|
||||
UserTypeShortName = user.UserTypeShortName
|
||||
};
|
||||
}
|
||||
public static IRaCISClaims Create(DoctorAccountDTO doctor)
|
||||
{
|
||||
return new IRaCISClaims
|
||||
{
|
||||
Id = doctor.Id,
|
||||
FullName = doctor.FirstName + doctor.LastName,
|
||||
Phone = doctor.Phone,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
|
||||
namespace IRaCIS.Core.Application.Auth
|
||||
{
|
||||
public class JwtSetting
|
||||
{
|
||||
/// <summary>
|
||||
/// 颁发者
|
||||
/// </summary>
|
||||
public string Issuer { get; set; } = String.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 接收者
|
||||
/// </summary>
|
||||
public string Audience { get; set; } = String.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 令牌密码
|
||||
/// </summary>
|
||||
public string SecurityKey { get; set; } = String.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 过期时间
|
||||
/// </summary>
|
||||
public int TokenExpireDays { get; set; }
|
||||
|
||||
//public Dictionary<string, object> Claims { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 签名
|
||||
/// </summary>
|
||||
public SigningCredentials Credentials
|
||||
{
|
||||
get
|
||||
{
|
||||
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecurityKey));
|
||||
return new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Claims;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Core.Infra.EFCore.AuthUser;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace IRaCIS.Core.Application.Auth
|
||||
{
|
||||
|
||||
public interface ITokenService
|
||||
{
|
||||
string GetToken(IRaCISClaims user);
|
||||
}
|
||||
|
||||
|
||||
public class TokenService : ITokenService
|
||||
{
|
||||
private readonly JwtSetting _jwtSetting;
|
||||
|
||||
public TokenService(IOptions<JwtSetting> option)
|
||||
{
|
||||
_jwtSetting = option.Value;
|
||||
}
|
||||
|
||||
public string GetToken(IRaCISClaims user)
|
||||
{
|
||||
//创建用户身份标识,可按需要添加更多信息
|
||||
var claims = new Claim[]
|
||||
{
|
||||
new Claim(Microsoft.IdentityModel.JsonWebTokens.JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
|
||||
new Claim(JwtIRaCISClaimType.Id, user.Id.ToString()),
|
||||
new Claim(JwtIRaCISClaimType.Name, user.FullName),
|
||||
new Claim(JwtIRaCISClaimType.RealName, user.RealName),
|
||||
new Claim(JwtIRaCISClaimType.Code,user.Code),
|
||||
new Claim(JwtIRaCISClaimType.UserTypeId,user.UserTypeId.ToString()),
|
||||
new Claim(JwtIRaCISClaimType.UserTypeEnum,user.UserTypeEnum.ToString()),
|
||||
new Claim(JwtIRaCISClaimType.UserTypeEnumInt,((int)user.UserTypeEnum).ToString()),
|
||||
new Claim(JwtIRaCISClaimType.UserTypeShortName,user.UserTypeShortName),
|
||||
new Claim(JwtIRaCISClaimType.PermissionStr,user.PermissionStr)
|
||||
};
|
||||
|
||||
////创建令牌
|
||||
var token = new JwtSecurityToken(
|
||||
issuer: _jwtSetting.Issuer,
|
||||
audience: _jwtSetting.Audience,
|
||||
signingCredentials: _jwtSetting.Credentials,
|
||||
claims: claims,
|
||||
notBefore: DateTime.Now,
|
||||
expires: DateTime.Now.AddDays(_jwtSetting.TokenExpireDays)
|
||||
);
|
||||
|
||||
string jwtToken = new JwtSecurityTokenHandler().WriteToken(token);
|
||||
return jwtToken;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
using EasyCaching.Core;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace IRaCIS.Application.Services.BackGroundJob
|
||||
{
|
||||
|
||||
public interface ICacheTrialStatusJob
|
||||
{
|
||||
Task MemoryCacheTrialStatus();
|
||||
}
|
||||
public class CacheTrialStatusHangfireJob: ICacheTrialStatusJob
|
||||
{
|
||||
private readonly IRepository<Trial> _trialRepository;
|
||||
private readonly IEasyCachingProvider _provider;
|
||||
private readonly ILogger<CacheTrialStatusHangfireJob> _logger;
|
||||
|
||||
public CacheTrialStatusHangfireJob(IRepository<Trial> trialRepository, IEasyCachingProvider provider,ILogger<CacheTrialStatusHangfireJob> logger)
|
||||
{
|
||||
_trialRepository = trialRepository;
|
||||
_provider = provider;
|
||||
_logger = logger;
|
||||
}
|
||||
public Task MemoryCacheTrialStatus()
|
||||
{
|
||||
_logger.LogInformation("hangfire 定时任务开始~");
|
||||
try
|
||||
{
|
||||
var list = _trialRepository.Select(t => new { TrialId = t.Id, TrialStatusStr = t.TrialStatusStr })
|
||||
.ToList();
|
||||
//_provider.GetCount("");
|
||||
|
||||
list.ForEach(t => _provider.Set(t.TrialId.ToString(), t.TrialStatusStr, TimeSpan.FromDays(7)));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError("hangfire 定时任务执行失败"+e.Message);
|
||||
|
||||
}
|
||||
|
||||
_logger.LogInformation("hangfire 定时任务执行结束");
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using EasyCaching.Core;
|
||||
using IRaCIS.Core.Domain;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Quartz;
|
||||
|
||||
namespace IRaCIS.Application.Services.BackGroundJob
|
||||
{
|
||||
|
||||
public class CacheTrialStatusQuartZJob: IJob
|
||||
{
|
||||
|
||||
private readonly IRepository<Trial> _trialRepository;
|
||||
private readonly IEasyCachingProvider _provider;
|
||||
private readonly ILogger<CacheTrialStatusQuartZJob> _logger;
|
||||
|
||||
public CacheTrialStatusQuartZJob(IRepository<Trial> trialRepository, IEasyCachingProvider provider,ILogger<CacheTrialStatusQuartZJob> logger)
|
||||
{
|
||||
_trialRepository = trialRepository;
|
||||
_provider = provider;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Task Execute(IJobExecutionContext context)
|
||||
|
||||
{
|
||||
_logger.LogInformation($"开始执行QuartZ定时任务作业");
|
||||
try
|
||||
{
|
||||
var list = _trialRepository.Select(t => new { TrialId = t.Id, TrialStatusStr = t.TrialStatusStr })
|
||||
.ToList();
|
||||
//_provider.GetCount("");
|
||||
|
||||
list.ForEach(t => _provider.Set(t.TrialId.ToString(), t.TrialStatusStr, TimeSpan.FromDays(1)));
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError($" 查询和缓存过程出现异常"+e.Message);
|
||||
}
|
||||
_logger.LogInformation("QuartZ定时任务作业结束");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
using IRaCIS.Core.Infra.EFCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace IRaCIS.Core.Application.BackGroundJob
|
||||
{
|
||||
public interface IObtainTaskAutoCancelJob
|
||||
{
|
||||
Task CancelQCObtaion(Guid subjectVisitId,DateTime startTime);
|
||||
}
|
||||
|
||||
public class ObtainTaskAutoCancelJob : IObtainTaskAutoCancelJob
|
||||
{
|
||||
private readonly IRepository<SubjectVisit> _subjectVisitRepository;
|
||||
private readonly ILogger<ObtainTaskAutoCancelJob> _logger;
|
||||
|
||||
public ObtainTaskAutoCancelJob(IRepository<SubjectVisit> subjectVisitRepository, ILogger<ObtainTaskAutoCancelJob> logger)
|
||||
{
|
||||
_subjectVisitRepository = subjectVisitRepository;
|
||||
_logger = logger;
|
||||
}
|
||||
public async Task CancelQCObtaion(Guid subjectVisitId, DateTime startTime)
|
||||
{
|
||||
try
|
||||
{
|
||||
var dbSubjectVisit = await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == subjectVisitId).IfNullThrowException();
|
||||
|
||||
dbSubjectVisit.IsTake = false;
|
||||
dbSubjectVisit.CurrentActionUserId = null;
|
||||
dbSubjectVisit.CurrentActionUserExpireTime = null;
|
||||
|
||||
var success = await _subjectVisitRepository.SaveChangesAsync();
|
||||
|
||||
_logger.LogWarning($"任务建立时间:{startTime} 取消时间:{DateTime.Now} 取消 受试者访视:{ subjectVisitId }success:{success}");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError("hangfire 定时任务执行失败" + e.Message);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
using AutoMapper;
|
||||
using IRaCIS.Application.Services.BusinessFilter;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Panda.DynamicWebApi;
|
||||
using Panda.DynamicWebApi.Attributes;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace IRaCIS.Core.Application
|
||||
{
|
||||
|
||||
#pragma warning disable CS8618
|
||||
|
||||
|
||||
#region 非泛型版本
|
||||
|
||||
[Authorize, DynamicWebApi, UnifiedApiResultFilter]
|
||||
public class BaseService : IBaseService, IDynamicWebApi
|
||||
{
|
||||
public IMapper _mapper { get; set; }
|
||||
|
||||
public IUserInfo _userInfo { get; set; }
|
||||
|
||||
public IRepository _repository { get; set; }
|
||||
|
||||
public IStringLocalizer _localizer { get; set; }
|
||||
|
||||
|
||||
|
||||
|
||||
public static IResponseOutput Null404NotFound<TEntity>(TEntity? businessObject) where TEntity : class
|
||||
{
|
||||
return new ResponseOutput<string>()
|
||||
.NotOk($"The query object {typeof(TEntity).Name} does not exist , or was deleted by someone else, or an incorrect parameter query caused", code: ApiResponseCodeEnum.DataNotExist);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public interface IBaseService
|
||||
{
|
||||
[MemberNotNull(nameof(_mapper))]
|
||||
public IMapper _mapper { get; set; }
|
||||
|
||||
[MemberNotNull(nameof(_userInfo))]
|
||||
public IUserInfo _userInfo { get; set; }
|
||||
|
||||
[MemberNotNull(nameof(_repository))]
|
||||
public IRepository _repository { get; set; }
|
||||
|
||||
[MemberNotNull(nameof(_localizer))]
|
||||
public IStringLocalizer _localizer { get; set; }
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region 泛型版本测试
|
||||
|
||||
|
||||
public interface IBaseServiceTest<T> where T : Entity
|
||||
{
|
||||
[MemberNotNull(nameof(_mapper))]
|
||||
public IMapper _mapper { get; set; }
|
||||
|
||||
[MemberNotNull(nameof(_userInfo))]
|
||||
public IUserInfo _userInfo { get; set; }
|
||||
|
||||
[MemberNotNull(nameof(_repository))]
|
||||
public IRepository _repository { get; set; }
|
||||
|
||||
[MemberNotNull(nameof(_localizer))]
|
||||
public IStringLocalizer _localizer { get; set; }
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
[Authorize, DynamicWebApi, UnifiedApiResultFilter]
|
||||
public class BaseServiceTest<T> : IBaseServiceTest<T>, IDynamicWebApi where T : Entity
|
||||
{
|
||||
public IMapper _mapper { get; set; }
|
||||
|
||||
public IUserInfo _userInfo { get; set; }
|
||||
|
||||
public IRepository _repository { get; set; }
|
||||
|
||||
public IStringLocalizer _localizer { get; set; }
|
||||
|
||||
public static IResponseOutput Null404NotFound<TEntity>(TEntity? businessObject) where TEntity : class
|
||||
{
|
||||
return new ResponseOutput<string>()
|
||||
.NotOk($"The query object {typeof(TEntity).Name} does not exist , or was deleted by someone else, or an incorrect parameter query caused", code: ApiResponseCodeEnum.DataNotExist);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
//using System.Diagnostics;
|
||||
//using IRaCIS.Application.Interfaces;
|
||||
//using IRaCIS.Application.Contracts;
|
||||
//using IRaCIS.Core.Infra.EFCore;
|
||||
//using IRaCIS.Core.Infrastructure.Extention;
|
||||
//using Microsoft.AspNetCore.Mvc;
|
||||
//using Microsoft.AspNetCore.Mvc.Filters;
|
||||
//using Microsoft.Extensions.Logging;
|
||||
//using Newtonsoft.Json;
|
||||
|
||||
//namespace IRaCIS.Core.Application.Filter
|
||||
//{
|
||||
|
||||
// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
|
||||
// public class LogFilter : Attribute
|
||||
// {
|
||||
// }
|
||||
// public class LogActionFilter : IAsyncActionFilter
|
||||
// {
|
||||
// private readonly ILogService _logService;
|
||||
// private readonly IUserInfo _userInfo;
|
||||
// private readonly ILogger<LogActionFilter> _logger;
|
||||
|
||||
// public LogActionFilter(ILogService logService, IUserInfo userInfo , ILogger<LogActionFilter> logger)
|
||||
// {
|
||||
// _logService = logService;
|
||||
// _userInfo = userInfo;
|
||||
// _logger = logger;
|
||||
// }
|
||||
|
||||
// public Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
|
||||
// {
|
||||
|
||||
// if (context.ActionDescriptor.EndpointMetadata!=null&& context.ActionDescriptor.EndpointMetadata.Any(m => m.GetType() == typeof(LogFilter)))
|
||||
// {
|
||||
// return LogAsync(context, next);
|
||||
// }
|
||||
// return next();
|
||||
// }
|
||||
|
||||
// public async Task LogAsync(ActionExecutingContext context, ActionExecutionDelegate next)
|
||||
// {
|
||||
// var sw = new Stopwatch();
|
||||
// sw.Start();
|
||||
|
||||
// dynamic actionResult = (await next()).Result;
|
||||
// sw.Stop();
|
||||
// var args = JsonConvert.SerializeObject(context.ActionArguments);
|
||||
// var result = JsonConvert.SerializeObject(actionResult?.Value);
|
||||
|
||||
// var attr = (ApiExplorerSettingsAttribute)context.ActionDescriptor.EndpointMetadata.FirstOrDefault(m => m.GetType() == typeof(ApiExplorerSettingsAttribute));
|
||||
// var groupName = attr?.GroupName;
|
||||
// var res = actionResult?.Value as IResponseOutput;
|
||||
// var input = new SystemLogDTO
|
||||
// {
|
||||
// ClientIP = string.Empty,
|
||||
// OptUserId = _userInfo.Id,
|
||||
// OptUserName = _userInfo.UserName,
|
||||
// ApiPath = context.ActionDescriptor.AttributeRouteInfo.Template.ToLower(),
|
||||
// Params = args,
|
||||
// Result = result,
|
||||
// RequestTime = DateTime.Now,
|
||||
// ElapsedMilliseconds = sw.ElapsedMilliseconds,
|
||||
// Status =res?.IsSuccess?? false,
|
||||
// Message = res?.ErrorMessage,
|
||||
// LogCategory = groupName
|
||||
// };
|
||||
|
||||
// try
|
||||
// {
|
||||
// _logService.SaveLog2Db(input);
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
|
||||
// _logger.LogError(ex.Message);
|
||||
// }
|
||||
|
||||
// }
|
||||
// }
|
||||
//}
|
|
@ -0,0 +1,27 @@
|
|||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
|
||||
namespace IRaCIS.Core.Application.Filter
|
||||
{
|
||||
public class ModelActionFilter : ActionFilterAttribute, IActionFilter
|
||||
{
|
||||
public override void OnActionExecuting(ActionExecutingContext context)
|
||||
{
|
||||
if (!context.ModelState.IsValid)
|
||||
{
|
||||
|
||||
var validationErrors = context.ModelState
|
||||
.Keys
|
||||
.SelectMany(k => context.ModelState[k]!.Errors)
|
||||
.Select(e => e.ErrorMessage)
|
||||
.ToArray();
|
||||
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk("The inputs supplied to the API are invalid. " +JsonConvert.SerializeObject( validationErrors)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
|
||||
namespace IRaCIS.Core.Application.Filter
|
||||
{
|
||||
#region snippet_DisableFormValueModelBindingAttribute
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
|
||||
public class DisableFormValueModelBindingAttribute : Attribute, IResourceFilter
|
||||
{
|
||||
public void OnResourceExecuting(ResourceExecutingContext context)
|
||||
{
|
||||
|
||||
|
||||
var factories = context.ValueProviderFactories;
|
||||
//factories.RemoveType<FormValueProviderFactory>();
|
||||
factories.RemoveType<FormFileValueProviderFactory>();
|
||||
//factories.RemoveType<JQueryFormValueProviderFactory>();
|
||||
context.HttpContext.Request.EnableBuffering();
|
||||
}
|
||||
|
||||
public void OnResourceExecuted(ResourceExecutedContext context)
|
||||
{
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
namespace System.ComponentModel.DataAnnotations
|
||||
{
|
||||
[AttributeUsage(
|
||||
AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter,
|
||||
AllowMultiple = false)]
|
||||
public class GuidNotEmptyAttribute : ValidationAttribute
|
||||
{
|
||||
public const string DefaultErrorMessage = "The {0} field must not be empty";
|
||||
public GuidNotEmptyAttribute() : base(DefaultErrorMessage) { }
|
||||
|
||||
public override bool IsValid(object? value)
|
||||
{
|
||||
//NotEmpty doesn't necessarily mean required
|
||||
if (value is null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (value)
|
||||
{
|
||||
case Guid guid:
|
||||
return guid != Guid.Empty;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class NotDefaultAttribute : ValidationAttribute
|
||||
{
|
||||
public const string DefaultErrorMessage = "The {0} field is is not passed or not set a valid value";
|
||||
public NotDefaultAttribute() : base(DefaultErrorMessage) { }
|
||||
|
||||
public override bool IsValid(object? value)
|
||||
{
|
||||
//NotDefault doesn't necessarily mean required
|
||||
if (value is null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var type = value.GetType();
|
||||
if (type.IsValueType)
|
||||
{
|
||||
var defaultValue = Activator.CreateInstance(type);
|
||||
return !value.Equals(defaultValue);
|
||||
}
|
||||
|
||||
// non-null ref type
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
using IRaCIS.Core.Infrastructure;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace IRaCIS.Core.Application.Filter
|
||||
{
|
||||
public class ProjectExceptionFilter : Attribute, IExceptionFilter
|
||||
{
|
||||
private readonly ILogger<ProjectExceptionFilter> _logger;
|
||||
|
||||
public ProjectExceptionFilter(ILogger<ProjectExceptionFilter> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
public void OnException(ExceptionContext context)
|
||||
{
|
||||
//context.ExceptionHandled;//记录当前这个异常是否已经被处理过了
|
||||
|
||||
if (!context.ExceptionHandled)
|
||||
{
|
||||
if (context.Exception.GetType().Name == "DbUpdateConcurrencyException")
|
||||
{
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk("并发更新,当前不允许该操作" + context.Exception.Message));
|
||||
}
|
||||
|
||||
if (context.Exception.GetType() == typeof(BusinessValidationFailedException))
|
||||
{
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk("Verify error: " + context.Exception.Message));
|
||||
}
|
||||
else if(context.Exception.GetType() == typeof(QueryBusinessObjectNotExistException))
|
||||
{
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk( context.Exception.Message));
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk(" Program exception, please contact the developer! " + (context.Exception.InnerException is null? context.Exception.Message:context.Exception.InnerException?.Message) ));
|
||||
}
|
||||
|
||||
|
||||
_logger.LogError(context.Exception.InnerException is null ? (context.Exception.Message +context.Exception.StackTrace): (context.Exception.InnerException?.Message+ context.Exception.InnerException?.StackTrace));
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
//继续
|
||||
}
|
||||
context.ExceptionHandled = true;//标记当前异常已经被处理过了
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,152 @@
|
|||
using EasyCaching.Core;
|
||||
using IRaCIS.Core.Domain.Models;
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Core.Infra.EFCore;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
|
||||
namespace IRaCIS.Core.Application.Filter
|
||||
{
|
||||
/// <summary>
|
||||
/// 主要为了 处理项目结束 锁库,不允许操作
|
||||
/// </summary>
|
||||
public class TrialResourceFilter : Attribute, IAsyncResourceFilter /* , IResourceFilter*/
|
||||
{
|
||||
private readonly IEasyCachingProvider _provider;
|
||||
private readonly IUserInfo _userInfo;
|
||||
|
||||
|
||||
public TrialResourceFilter(IEasyCachingProvider provider, IUserInfo userInfo)
|
||||
{
|
||||
_provider = provider;
|
||||
_userInfo = userInfo;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//优先选择异步的方法
|
||||
public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
|
||||
{
|
||||
#region 处理新的用户类型,不能操作项目相关接口
|
||||
|
||||
if( _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA)
|
||||
{
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk("Sorry,Your UserType does not allow this operation"));
|
||||
|
||||
return ;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
//bool isEditTrialStatus = context.ActionDescriptor.DisplayName==null? false:context.ActionDescriptor.DisplayName.Contains("UpdateTrialStatus");
|
||||
|
||||
//bool isTrialAdd = context.ActionDescriptor.DisplayName == null ? false: context.ActionDescriptor.DisplayName.Contains("AddOrUpdateTrial");
|
||||
|
||||
//TrialId 传递的途径多种,可能在path 可能在body 可能在数组中,也可能在对象中,可能就在url
|
||||
var trialIdStr = string.Empty;
|
||||
|
||||
//先尝试从path中取TrialId
|
||||
if (context.RouteData.Values.Keys.Any(t => t.Contains("trialId")))
|
||||
{
|
||||
var index = context.RouteData.Values.Keys.ToList().IndexOf("trialId");
|
||||
trialIdStr = context.RouteData.Values.Values.ToList()[index] as string;
|
||||
}
|
||||
else
|
||||
{
|
||||
#region body 中取数据
|
||||
|
||||
//设置可以多次读
|
||||
context.HttpContext.Request.EnableBuffering();
|
||||
var reader = new StreamReader(context.HttpContext.Request.Body);
|
||||
var contentFromBody = await reader.ReadToEndAsync();
|
||||
//读取后,流的位置还原
|
||||
context.HttpContext.Request.Body.Seek(0, SeekOrigin.Begin);
|
||||
//context.HttpContext.Request.Body.Position = 0;
|
||||
|
||||
//找到参数位置在字符串中的索引
|
||||
var trialIdIndex = contentFromBody.IndexOf("\"TrialId\"", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (trialIdIndex > -1)
|
||||
{
|
||||
trialIdStr = contentFromBody.Substring(trialIdIndex + "TrialId".Length + 4, 36);
|
||||
}
|
||||
//else if(isTrialAdd)
|
||||
//{
|
||||
// //项目的添加和编辑时例外 trailId 在Id字段中 同时,添加和更新时有区别的
|
||||
// trialIdIndex = contentFromBody.IndexOf("\"Id\"");
|
||||
|
||||
// //添加时为-1 不进行操作
|
||||
// if (trialIdIndex != -1)
|
||||
// {
|
||||
|
||||
// trialIdStr = contentFromBody.Substring(trialIdIndex + "Id".Length + 4, 36);
|
||||
|
||||
// trialIdStr = Guid.TryParse(trialIdStr, out var trialId) ? trialId.ToString() : String.Empty ;
|
||||
// }
|
||||
|
||||
//}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
//通过path 或者body 找到trialId 了
|
||||
if (trialIdStr != string.Empty)
|
||||
{
|
||||
|
||||
//如果没缓存数据,是不允许的 意外情况,IIS回收了,导致定时任务没执行或者缓存丢失
|
||||
if (_provider.GetCount() == 0)
|
||||
{
|
||||
|
||||
var _trialRepository = context.HttpContext.RequestServices.GetService(typeof(IRepository<Trial>)) as IRepository<Trial>;
|
||||
|
||||
var list = _trialRepository.IfNullThrowException().Select(t => new { TrialId = t.Id, TrialStatusStr = t.TrialStatusStr })
|
||||
.ToList();
|
||||
|
||||
list.ForEach(t => _provider.Set(t.TrialId.ToString(), t.TrialStatusStr, TimeSpan.FromDays(7)));
|
||||
}
|
||||
|
||||
var cacheResultDic = _provider.GetAll<string>(new[] { trialIdStr });
|
||||
|
||||
var trialStatusStr = cacheResultDic[trialIdStr];
|
||||
|
||||
|
||||
//项目完成和停止,都不能操作
|
||||
if (trialStatusStr.Value == StaticData.TrialCompleted || trialStatusStr.Value == StaticData.TrialStopped)
|
||||
{
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk("Only trial in ongoing state can the operation be performed"));
|
||||
}
|
||||
|
||||
//仅仅管理员 在项目暂停 并且是编辑项目接口时 才放开操作 遗漏了正常情况 TrialOngoing
|
||||
if (/*(trialStatusStr.Value == StaticData.TrialPaused && _userInfo.UserTypeEnumInt == (int)UserTypeEnum.SuperAdmin && isEditTrialStatus)||*/ trialStatusStr.Value == StaticData.TrialOngoing)
|
||||
{
|
||||
|
||||
await next.Invoke();
|
||||
|
||||
}
|
||||
//项目暂停的基础上,是其他人,或者不是编辑项目状态,那么要禁止操作
|
||||
else
|
||||
{
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk("Only trial in ongoing state can the operation be performed"));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
////没有找到trialId 判断是否是项目添加
|
||||
//else if (isTrialAdd)
|
||||
//{
|
||||
// await next.Invoke();
|
||||
//}
|
||||
else
|
||||
{
|
||||
//如果项目相关接口没有传递trialId 会来到这里,提醒,以便修改
|
||||
|
||||
context.Result = new JsonResult(ResponseOutput.NotOk("该接口参数中,没有传递trialId,请核查"));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
using System;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using System.Threading.Tasks;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace IRaCIS.Application.Services.BusinessFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// 统一返回前端数据包装,之前在控制器包装,现在修改为动态Api 在ResultFilter这里包装,减少重复冗余代码
|
||||
/// by zhouhang 2021.09.12 周末
|
||||
/// </summary>
|
||||
public class UnifiedApiResultFilter : Attribute, IAsyncResultFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// 异步版本
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="next"></param>
|
||||
/// <returns></returns>
|
||||
public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
|
||||
{
|
||||
|
||||
if (context.Result is ObjectResult objectResult)
|
||||
{
|
||||
var statusCode = objectResult.StatusCode ?? context.HttpContext.Response.StatusCode;
|
||||
|
||||
//是200 并且没有包装 那么包装结果
|
||||
if (statusCode == 200 && !(objectResult.Value is IResponseOutput))
|
||||
{
|
||||
//if (objectResult.Value == null)
|
||||
//{
|
||||
// var apiResponse = ResponseOutput.DBNotExist();
|
||||
|
||||
// objectResult.Value = apiResponse;
|
||||
// objectResult.DeclaredType = apiResponse.GetType();
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
|
||||
var type = objectResult.Value?.GetType();
|
||||
|
||||
|
||||
if ( type!=null&& type.IsGenericType&&(type.GetGenericTypeDefinition()==typeof(ValueTuple<,>)|| type.GetGenericTypeDefinition()==typeof(Tuple<,>)))
|
||||
{
|
||||
|
||||
//报错
|
||||
//var tuple = (object, object))objectResult.Value;
|
||||
|
||||
//var (val1, val2) = ((dynamic, dynamic))objectResult.Value;
|
||||
//var apiResponse = ResponseOutput.Ok(val1, val2);
|
||||
|
||||
//OK
|
||||
var tuple = (dynamic)objectResult.Value;
|
||||
var apiResponse = ResponseOutput.Ok(tuple.Item1, tuple.Item2);
|
||||
|
||||
|
||||
objectResult.Value = apiResponse;
|
||||
objectResult.DeclaredType = apiResponse.GetType();
|
||||
}
|
||||
else
|
||||
{
|
||||
var apiResponse = ResponseOutput.Ok(objectResult.Value);
|
||||
|
||||
objectResult.Value = apiResponse;
|
||||
objectResult.DeclaredType = apiResponse.GetType();
|
||||
}
|
||||
|
||||
|
||||
//}
|
||||
|
||||
}
|
||||
//如果不是200 是IResponseOutput 不处理
|
||||
else if (statusCode != 200 && (objectResult.Value is IResponseOutput))
|
||||
{
|
||||
}
|
||||
|
||||
else if(statusCode != 200&&!(objectResult.Value is IResponseOutput))
|
||||
{
|
||||
var apiResponse = ResponseOutput.NotOk("Program error, contact the developer!");
|
||||
|
||||
objectResult.Value = apiResponse;
|
||||
objectResult.DeclaredType = apiResponse.GetType();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
await next.Invoke();
|
||||
|
||||
}
|
||||
|
||||
public static bool IsTupleType(Type type, bool checkBaseTypes = false)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException(nameof(type));
|
||||
|
||||
if (type == typeof(Tuple))
|
||||
return true;
|
||||
|
||||
while (type != null)
|
||||
{
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
var genType = type.GetGenericTypeDefinition();
|
||||
if (genType == typeof(Tuple<>)
|
||||
|| genType == typeof(Tuple<,>)
|
||||
|| genType == typeof(Tuple<,>))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!checkBaseTypes)
|
||||
break;
|
||||
|
||||
type = type.BaseType;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
using IRaCIS.Core.Infra.EFCore;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace IRaCIS.Core.Application.BusinessFilter
|
||||
{
|
||||
|
||||
//public class UserTypeRequirement : IAuthorizationRequirement
|
||||
//{
|
||||
//}
|
||||
|
||||
//public class UserTypeHandler : AuthorizationHandler<UserTypeRequirement>
|
||||
//{
|
||||
|
||||
// private IUserInfo _userInfo;
|
||||
|
||||
// public UserTypeHandler(IUserInfo userInfo)
|
||||
// {
|
||||
// _userInfo = userInfo;
|
||||
// }
|
||||
|
||||
|
||||
// protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, UserTypeRequirement requirement)
|
||||
// {
|
||||
|
||||
// //if (context.User.Claims.Count() == 0)
|
||||
// //{
|
||||
// // return Task.CompletedTask;
|
||||
// //}
|
||||
|
||||
// //string userId = context.User.Claims.First(c => c.Type == "Userid").Value;
|
||||
// //string qq = context.User.Claims.First(c => c.Type == "QQ").Value;
|
||||
|
||||
// //if (_UserService.Validata(userId, qq))
|
||||
// //{
|
||||
// // context.Succeed(requirement); //验证通过了
|
||||
// //}
|
||||
// ////在这里就可以做验证
|
||||
|
||||
// return Task.CompletedTask;
|
||||
// }
|
||||
//}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<LangVersion>default</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<OutputPath>..\bin\</OutputPath>
|
||||
<DocumentationFile>.\IRaCIS.Core.Application.xml</DocumentationFile>
|
||||
<NoWarn>1701;1702;1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<NoWarn>1701;1702;1591;1587</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
<Using Include="IRaCIS.Core.Application;" />
|
||||
<Using Include="AutoMapper.QueryableExtensions;" />
|
||||
<Using Include="Microsoft.EntityFrameworkCore;" />
|
||||
<Using Include="IRaCIS.Core.Domain.Models;" />
|
||||
<Using Include="IRaCIS.Core.Infrastructure.Extention;" />
|
||||
|
||||
<!-- Global using -->
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="WebAppConfig.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="IRaCIS.Core.Application.xml" />
|
||||
<None Remove="Resources\en-US.json" />
|
||||
<None Remove="Resources\zh-CN.json" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Resources\zh-CN.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
</Content>
|
||||
<Content Include="Resources\en-US.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Autofac.Extras.DynamicProxy" Version="6.0.0" />
|
||||
<PackageReference Include="Castle.Core.AsyncInterceptor" Version="2.0.0" />
|
||||
<PackageReference Include="EasyCaching.Interceptor.AspectCore" Version="1.4.1" />
|
||||
<PackageReference Include="Efferent.Native" Version="4.1.0" />
|
||||
<PackageReference Include="ExcelDataReader" Version="3.6.0" />
|
||||
<PackageReference Include="ExcelDataReader.DataSet" Version="3.6.0" />
|
||||
<PackageReference Include="fo-dicom.Codecs" Version="5.0.3" />
|
||||
<PackageReference Include="fo-dicom.Drawing" Version="4.0.8" />
|
||||
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.0.2" />
|
||||
<PackageReference Include="Hangfire" Version="1.7.28" />
|
||||
<PackageReference Include="Magicodes.IE.Core" Version="2.6.1" />
|
||||
<PackageReference Include="Magicodes.IE.Excel" Version="2.6.1" />
|
||||
<PackageReference Include="Magicodes.IE.Excel.AspNetCore" Version="2.6.1" />
|
||||
<PackageReference Include="MailKit" Version="3.1.0" />
|
||||
<PackageReference Include="MediatR" Version="10.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.JsonPatch" Version="6.0.1" />
|
||||
<PackageReference Include="MimeKit" Version="3.1.0" />
|
||||
<PackageReference Include="MiniExcel" Version="0.19.1" />
|
||||
<PackageReference Include="My.Extensions.Localization.Json" Version="3.0.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="Panda.DynamicWebApi" Version="1.1.2" />
|
||||
<PackageReference Include="Quartz" Version="3.3.3" />
|
||||
<PackageReference Include="SharpCompress" Version="0.30.1" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="7.0.2" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.2.3" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.15.0" />
|
||||
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.2.15" />
|
||||
<PackageReference Include="UAParser" Version="3.1.47" />
|
||||
<PackageReference Include="WinSCP" Version="5.19.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\IRaCIS.Core.Infra.EFCore\IRaCIS.Core.Infra.EFCore.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
"test{0}": "英文本地化{0}",
|
||||
"RequiredAttribute": "{0} is required",
|
||||
|
||||
// SiteSurvey 服务--------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
// TrialSiteEquipmentSurveyService
|
||||
"CPMNotOperation": "CPM/APM Disallow operation", //CPM/APM 不允许操作
|
||||
"IsLockNotOperation": "The operation cannot be performed if it is locked", //已锁定 不允许操作
|
||||
|
||||
// TrialSiteSurveyService
|
||||
"ValidationEmail": "Please input a legal email", // 验证邮箱
|
||||
"ValidationPhone": "Please input a legal phone", // 验证手机号
|
||||
"SiteNotExistUpdateDisable": "The project Site does not have the survey record of the handover person, so it is not allowed to choose to update", // site不存在禁止更新
|
||||
"RecordLockUpdateDisable": "Your record is not locked, you are not allowed to choose to update, if submitted, can be rejected after operation", //记录锁定禁止更新
|
||||
"SiteLockUpdateDisableOther": "At the current Site, your survey record has been locked, and it is not allowed to update other people's email survey record", //Site锁定禁止更新其他人
|
||||
"SiteLockUpdateDisableSelf": "At the current Site, your survey record has been locked, and there are other unlocked records, so you are not allowed to update your own survey record", //Site锁定禁止更新自己
|
||||
"SiteLockUpdateDisableEmail": "当前Site 存在未锁定的调研记录,不允许更新已锁定邮箱的调研记录", //Site锁定禁止更新邮箱调研记录
|
||||
"EmailNotLatestDisableEmail{0}": "该邮箱{0}对应的调查表不是最新锁定的记录,不允许更新!", //邮箱调研表不是最新 不允许更新
|
||||
"SiteExistOtherUpdateDisable": "该Site下已经有其他用户已填写的调研表,您不被允许继续填写", //存在其他用户调研表不允许填写
|
||||
"IsLockUpdateDisable": "已锁定,不允许操作", //已锁定,不允许操作
|
||||
"OnlyAbolishNotFiled": "只允许废除未提交的记录", //只允许废除未提交的记录
|
||||
"AdminOperateDisable": "不允许Admin操作", // 不允许Admin操作
|
||||
"UserWrongNotSubmit": "人员信息有不正确项,不允许提交", // 人员信息有不正确项,不允许提交
|
||||
"FillInTheType": "请填写生成账号的类型。人员姓名{0}", // 请填写生成账号的类型。人员姓名
|
||||
|
||||
// TrialSiteUserSurveyService
|
||||
"InfoInconformity": "该用户在系统中账户名为:{0} ,与填写信息存在不一致项, 现将界面信息修改为与系统一致,可进行保存", // 信息不一致 修改一致进行保存
|
||||
|
||||
|
||||
// TrialSiteUser 服务------------------------------------------------------------------------------------------------------------
|
||||
|
||||
// TrialConfigService
|
||||
"NoDataDound": "未在系统中找到该签名场景的数据", //未在系统中找到该签名场景的数据
|
||||
"PasswordError": "password error", //密码错误
|
||||
"UserBeDisabled": "The user has been disabled!",
|
||||
"ProjSetDisable": "项目不在Initializing/Ongoing,不允许确认配置", //不允许确认配置
|
||||
"QCRepate": "QC问题显示序号不允许重复", // QC问题显示序号不允许重复
|
||||
"ParentNumToLow": "父问题的序号要比子问题序号小,请确认", // 父问题的序号要比子问题序号小,请确认
|
||||
"ExistUnconfirmedItems": "项目、基础配置、流程配置、加急配置、访视计划,有未确认项", // 存在未确认项
|
||||
"NotAllCanOperate": "only in Initializing or Ongoing State can operate" //只有“初始化中”和“正在进行中”才能进行操作
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
{
|
||||
"test{0}": "中文本地化{0}",
|
||||
"RequiredAttribute": "{0} 字段是必须的",
|
||||
// SiteSurvey 服务--------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
// TrialSiteEquipmentSurveyService
|
||||
"CPMNotOperation": "CPM/APM 不允许操作", //CPM/APM 不允许操作
|
||||
"IsLockNotOperation": "已锁定 不允许操作", //已锁定 不允许操作
|
||||
|
||||
// TrialSiteSurveyService
|
||||
"ValidationEmail": "请输入正确的邮箱", // 验证邮箱
|
||||
"ValidationPhone": "请输入正确的手机号", // 验证手机号
|
||||
"SiteNotExistUpdateDisable": "该项目Site不存在该交接人的调研记录,不允许选择更新", // site不存在禁止更新
|
||||
"RecordLockUpdateDisable": "您的记录未锁定,不允许选择更新,若已经提交,可被驳回后进行操作", //记录锁定禁止更新
|
||||
"SiteLockUpdateDisableOther": "当前Site 您的调研记录已锁定,不允许更新其他人邮箱调研记录", //Site锁定禁止更新其他人
|
||||
"SiteLockUpdateDisableSelf": "当前Site 您的调研记录已锁定,也存在其他未锁定的记录,不允许更新自己的调研记录", //Site锁定禁止更新自己
|
||||
"SiteLockUpdateDisableEmail": "当前Site 存在未锁定的调研记录,不允许更新已锁定邮箱的调研记录", //Site锁定禁止更新邮箱调研记录
|
||||
"EmailNotLatestDisableEmail{0}": "该邮箱{0}对应的调查表不是最新锁定的记录,不允许更新!", //邮箱调研表不是最新 不允许更新
|
||||
"SiteExistOtherUpdateDisable": "该Site下已经有其他用户已填写的调研表,您不被允许继续填写", //存在其他用户调研表不允许填写
|
||||
"IsLockUpdateDisable": "已锁定,不允许操作", //已锁定,不允许操作
|
||||
"OnlyAbolishNotFiled": "只允许废除未提交的记录", //只允许废除未提交的记录
|
||||
"AdminOperateDisable": "不允许Admin操作", // 不允许Admin操作
|
||||
"UserWrongNotSubmit": "人员信息有不正确项,不允许提交", // 人员信息有不正确项,不允许提交
|
||||
"FillInTheType{0}": "请填写生成账号的类型。人员姓名{0}", // 请填写生成账号的类型。人员姓名
|
||||
|
||||
// TrialSiteUserSurveyService
|
||||
"InfoInconformity{0}": "该用户在系统中账户名为:{0} ,与填写信息存在不一致项, 现将界面信息修改为与系统一致,可进行保存", // 信息不一致 修改一致进行保存
|
||||
|
||||
|
||||
// TrialSiteUser 服务------------------------------------------------------------------------------------------------------------
|
||||
|
||||
// TrialConfigService
|
||||
"NoDataDound": "未在系统中找到该签名场景的数据", //未在系统中找到该签名场景的数据
|
||||
"PasswordError": "密码错误", //密码错误
|
||||
"UserBeDisabled": "用户被禁用", // 用户被禁用
|
||||
"ProjSetDisable": "项目不在Initializing/Ongoing,不允许确认配置", //不允许确认配置
|
||||
"QCRepate": "QC问题显示序号不允许重复", // QC问题显示序号不允许重复
|
||||
"ParentNumToLow": "父问题的序号要比子问题序号小,请确认", // 父问题的序号要比子问题序号小,请确认
|
||||
"ExistUnconfirmedItems": "项目、基础配置、流程配置、加急配置、访视计划,有未确认项", // 存在未确认项
|
||||
"NotAllCanOperate": "只有“初始化中”和“正在进行中”才能进行操作", //只有“初始化中”和“正在进行中”才能进行操作
|
||||
|
||||
// TrialExternalUserService
|
||||
"InfoDoNotAgree{0}{1}": "该用户在系统中账户名为:{0} 电话:{1},与填写信息存在不一致项, 现将界面信息修改为与系统一致,可进行保存" // 用户信息不一致
|
||||
"UserExist"
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
using IRaCIS.Core.Domain.Share;
|
||||
using IRaCIS.Core.Infrastructure.Extention;
|
||||
|
||||
namespace IRaCIS.Application.Contracts
|
||||
{
|
||||
|
||||
|
||||
public class BasicDicView: AddOrEditBasicDic
|
||||
{
|
||||
|
||||
public DateTime CreateTime { get; set; }
|
||||
public Guid CreateUserId { get; set; }
|
||||
public DateTime UpdateTime { get; set; }
|
||||
public Guid UpdateUserId { get; set; }
|
||||
|
||||
public string ConfigType { get; set; }
|
||||
|
||||
public string ConfigTypeDes { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class AddOrEditBasicDic
|
||||
{
|
||||
public Guid? Id { get; set; }
|
||||
|
||||
public string Code { get; set; } = String.Empty;
|
||||
|
||||
public string KeyName { get; set; } = String.Empty;
|
||||
|
||||
public string Description { get; set; } = String.Empty;
|
||||
|
||||
|
||||
|
||||
public string Value { get; set; } = String.Empty;
|
||||
|
||||
public string ValueCN { get; set; } = String.Empty;
|
||||
|
||||
|
||||
|
||||
public int ShowOrder { get; set; }
|
||||
|
||||
|
||||
//有父亲 就有值
|
||||
public Guid? ParentId { get; set; }
|
||||
|
||||
public bool IsEnable { get; set; }
|
||||
|
||||
|
||||
//默认不是字典项 类型配置
|
||||
public bool IsConfig { get; set; }
|
||||
|
||||
//是配置的话,就有值
|
||||
public Guid? ConfigTypeId { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public class BasicDicSelect
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string KeyName { get; set; } = string.Empty;
|
||||
public string ValueCN { get; set; } = string.Empty;
|
||||
public string Value { get; set; } = string.Empty;
|
||||
public string Code { get; set; } = string.Empty;
|
||||
|
||||
public int ShowOrder { get; set; }
|
||||
|
||||
public Guid? ParentId { get; set; }
|
||||
public string ParentCode { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class BasicDicQuery:PageInput
|
||||
{
|
||||
public string? Code { get; set; }
|
||||
|
||||
public string? KeyName { get; set; }
|
||||
|
||||
public bool? IsConfig { get; set; }
|
||||
|
||||
public Guid? ConfigTypeId { get; set; }
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public class DicViewModelDTO : AddOrUpdateDicDTO
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
public class AddOrUpdateDicDTO
|
||||
{
|
||||
public Guid? Id { get; set; }
|
||||
public string KeyName { get; set; } = String.Empty;
|
||||
public string Value { get; set; } = String.Empty;
|
||||
public string Description { get; set; } = String.Empty;
|
||||
public string ValueCN { get; set; } = String.Empty;
|
||||
public int ShowOrder { get; set; }
|
||||
public string Type { get; set; } = String.Empty;
|
||||
}
|
||||
|
||||
public class DicQueryDTO : PageInput
|
||||
{
|
||||
public string KeyName { get; set; } = String.Empty;
|
||||
}
|
||||
|
||||
public class KeyNameType
|
||||
{
|
||||
public Guid KeyId { get; set; }
|
||||
public string KeyName { get; set; } = String.Empty;
|
||||
public string Type { get; set; } = String.Empty;
|
||||
}
|
||||
|
||||
public class DicResultDTO
|
||||
{
|
||||
public Dictionary<string, Dictionary<Guid, string>> DicList = new Dictionary<string, Dictionary<Guid, string>>();
|
||||
}
|
||||
|
||||
|
||||
public class TrialDictionaryView
|
||||
{
|
||||
public Guid? Id { get; set; }
|
||||
public string KeyName { get; set; } = String.Empty;
|
||||
public string Value { get; set; } = String.Empty;
|
||||
public int ShowOrder { get; set; }
|
||||
}
|
||||
|
||||
public class TrialDicSelect
|
||||
{
|
||||
public TrialDictionaryView[] Phase { get; set; } = new TrialDictionaryView[0];
|
||||
public TrialDictionaryView[] IndicationType { get; set; } = new TrialDictionaryView[0];
|
||||
public TrialDictionaryView[] DeclarationType { get; set; } = new TrialDictionaryView[0];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
//--------------------------------------------------------------------
|
||||
// 此代码由T4模板自动生成 byzhouhang 20210918
|
||||
// 生成时间 2022-02-15 11:55:57
|
||||
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
|
||||
//--------------------------------------------------------------------
|
||||
using Newtonsoft.Json;
|
||||
namespace IRaCIS.Core.Application.Contracts
|
||||
{
|
||||
/// <summary> EmailNoticeConfigView 列表视图模型 </summary>
|
||||
public class EmailNoticeConfigView : EmailNoticeConfigAddOrEdit
|
||||
{
|
||||
|
||||
public DateTime CreateTime { get; set; }
|
||||
public Guid CreateUserId { get; set; }
|
||||
public Guid UpdateUserId { get; set; }
|
||||
|
||||
public DateTime UpdateTime { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public SystemBasicDataSelect Scenario { get; set; }
|
||||
|
||||
|
||||
//public Guid? ScenarioParentId => Scenario.ParentId;
|
||||
|
||||
public string ScenarioName => Scenario.Value;
|
||||
public string ScenarioNameCN => Scenario.ValueCN;
|
||||
|
||||
}
|
||||
|
||||
///<summary>EmailNoticeConfigQuery 列表查询参数模型</summary>
|
||||
public class EmailNoticeConfigQuery:PageInput
|
||||
{
|
||||
public Guid? ScenarioId { get; set; }
|
||||
|
||||
public bool? IsReturnRequired { get; set; }
|
||||
public bool? IsUrgent { get; set; }
|
||||
public bool? IsEnable { get; set; }
|
||||
|
||||
}
|
||||
|
||||
///<summary> EmailNoticeConfigAddOrEdit 列表查询参数模型</summary>
|
||||
public class EmailNoticeConfigAddOrEdit
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string Code { get; set; } = String.Empty;
|
||||
|
||||
public string AuthorizationCode { get; set; } = String.Empty;
|
||||
|
||||
public Guid ScenarioId { get; set; }
|
||||
public string Title { get; set; } = String.Empty;
|
||||
public string Body { get; set; } = String.Empty;
|
||||
public string FromEmail { get; set; } = String.Empty;
|
||||
public string ReceiveEmail { get; set; } = String.Empty;
|
||||
public string CopyEmail { get; set; } = String.Empty;
|
||||
public bool IsReturnRequired { get; set; }
|
||||
public bool IsUrgent { get; set; }
|
||||
public bool IsEnable { get; set; }
|
||||
public bool IsAutoSend { get; set; }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
|
||||
namespace IRaCIS.Application.Contracts
|
||||
{
|
||||
|
||||
|
||||
public class UploadFileInfoDTO
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string FilePath { get; set; } = string.Empty;
|
||||
|
||||
//[JsonIgnore]
|
||||
//public string FullFilePathNoToken => FilePath;
|
||||
public string FullFilePath { get; set; } = string.Empty;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
|
||||
namespace IRaCIS.Application.Contracts
|
||||
{
|
||||
public class SysMessageDTO
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int ToDoctorId { get; set; }
|
||||
public int FromUserId { get; set; }
|
||||
public string Title { get; set; } = string.Empty;
|
||||
public string Content { get; set; } = string.Empty;
|
||||
public string MessageTime { get; set; } = string.Empty;
|
||||
public bool HasRead { get; set; }
|
||||
public string Memo { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
//--------------------------------------------------------------------
|
||||
// 此代码由T4模板自动生成 byzhouhang 20210918
|
||||
// 生成时间 2022-02-15 15:46:00
|
||||
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
|
||||
//--------------------------------------------------------------------
|
||||
using IRaCIS.Core.Domain.Share;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
namespace IRaCIS.Core.Application.Contracts
|
||||
{
|
||||
/// <summary> SystemBasicDataView 列表视图模型 </summary>
|
||||
public class SystemBasicDataView: SystemBasicDataAddOrEdit
|
||||
{
|
||||
|
||||
public DateTime CreateTime { get; set; }
|
||||
public Guid CreateUserId { get; set; }
|
||||
public DateTime UpdateTime { get; set; }
|
||||
public Guid UpdateUserId { get; set; }
|
||||
|
||||
}
|
||||
|
||||
|
||||
public class SystemBasicDataSelect
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string ValueCN { get; set; } = string.Empty;
|
||||
public string Value { get; set; } = string.Empty;
|
||||
public string Code { get; set; } = string.Empty;
|
||||
|
||||
public Guid? ParentId { get; set; }
|
||||
public string ParentCode { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
|
||||
///<summary>SystemBasicDataQuery 列表查询参数模型</summary>
|
||||
public class SystemBasicDataQuery:PageInput
|
||||
{
|
||||
///<summary> Name</summary>
|
||||
public string? Name { get; set; }
|
||||
|
||||
///<summary> Code</summary>
|
||||
public string? Code { get; set; }
|
||||
|
||||
}
|
||||
|
||||
///<summary> SystemBasicDataAddOrEdit 列表查询参数模型</summary>
|
||||
public class SystemBasicDataAddOrEdit
|
||||
{
|
||||
public Guid? Id { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string Value { get; set; } = string.Empty;
|
||||
public string Description { get; set; } = string.Empty;
|
||||
public int ShowOrder { get; set; }
|
||||
public string Code { get; set; } = string.Empty;
|
||||
public Guid? ParentId { get; set; }
|
||||
|
||||
public string ValueCN { get; set; } = string.Empty;
|
||||
|
||||
public bool IsEnable { get; set; }=true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue