Compare commits
269 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| df04878154 | |||
| ad3dae4fcd | |||
| 4af09e64a3 | |||
| 520595796b | |||
| d22ef17e91 | |||
| c0de59b3aa | |||
| 4e29d4094e | |||
| 84d4678406 | |||
| 53a5800a38 | |||
| fcaa6339c6 | |||
| 95042fe6ba | |||
| 7b779d4ef5 | |||
| 3a978b561b | |||
| 71e6099bbd | |||
| c713d0bc95 | |||
| c6c8daaadf | |||
| 67667f6438 | |||
| 2a1047f319 | |||
| 68b960abec | |||
| 419fef2e64 | |||
| d1b3256e52 | |||
| 871990287a | |||
| 6a54b0a6c8 | |||
| bec9af414f | |||
| 94293830d6 | |||
| 4fb018eaa6 | |||
| c65d6c4aa2 | |||
| cc4d879def | |||
| ba588e3e14 | |||
| fc392e869c | |||
| 052bff9247 | |||
| d00f81b09a | |||
| 6cde1b4405 | |||
| 3ef765e767 | |||
| 8f7e229513 | |||
| 0fdfeca3ec | |||
| 77bcdf79e8 | |||
| 20cc430132 | |||
| 969d22fdae | |||
| 9f95dc90bb | |||
| 0b1c4f2c09 | |||
| 14d9d8e7a5 | |||
| 0cc2e2c7ee | |||
| 0dfd33ced6 | |||
| a96c5eefc3 | |||
| 6d628727bb | |||
| ae83489c74 | |||
| 253a401c95 | |||
| 1209b07c03 | |||
| 44f3b58bad | |||
| 31e2e03536 | |||
| 2a172acb15 | |||
| 792a4235d2 | |||
| 6003bf291a | |||
| 42ab61d0a2 | |||
| 08158a58d6 | |||
| b4269c4ed0 | |||
| 7f0e998eba | |||
| a3c0bacff2 | |||
| 5c898ec6c8 | |||
| 68353b9e7b | |||
| f9089a9700 | |||
| 57aedd7dd4 | |||
| 8329362c33 | |||
| 787081a485 | |||
| 94aa943410 | |||
| 08a916d699 | |||
| 77a11cacc2 | |||
| a28f2e3bb2 | |||
| f060380735 | |||
| 57737b0d04 | |||
| 5023f5ed31 | |||
| cf6a7d9166 | |||
| a4c12dcbeb | |||
| 70240da5c5 | |||
| 0b2c707ec7 | |||
| aa1ecaf1fe | |||
| 39435b0068 | |||
| 9fc6ee267e | |||
| 1364031439 | |||
| 9c95d22b6a | |||
| f8108da641 | |||
| 39bb3bf56b | |||
| 2c37765969 | |||
| 3bb02ccb24 | |||
| 23ac7a73a7 | |||
| dacbe132a6 | |||
| 5f6d8b921f | |||
| fcb603abc6 | |||
| 5ceb5e790a | |||
| 2b1b5dce52 | |||
| 293a8bce31 | |||
| a7cfc1714a | |||
| ad01628e48 | |||
| 558258fd89 | |||
| 2a853e49f6 | |||
| acf9709d3a | |||
| 6c4ead5ab4 | |||
| 2ac995bf39 | |||
| 3ee175f091 | |||
| a701c082d9 | |||
| 9385e22c58 | |||
| 61278375ba | |||
| 9a74b81e93 | |||
| 981c029492 | |||
| 2fd6f70995 | |||
| b809a3b102 | |||
| 2d90ecd90f | |||
| 5ee5bac4ad | |||
| 8e764e5707 | |||
| c4501321ed | |||
| b0ece8bcfe | |||
| cce4872341 | |||
| 3c49192360 | |||
| c610c3bac0 | |||
| 09405dea8b | |||
| 06cb92b7e0 | |||
| f7a3e87ba8 | |||
| 2bad8065a5 | |||
| 22998ccb71 | |||
| 38b4268c67 | |||
| fb3228e18a | |||
| 056feade12 | |||
| 6d98b140e1 | |||
| 698ce08d30 | |||
| 6eddf7e2a1 | |||
| e26c9b6c1a | |||
| 62d3b69bd5 | |||
| ab4e1f8649 | |||
| 773c657b67 | |||
| a90bb18154 | |||
| 3dcf94f637 | |||
| 5e384db795 | |||
| abd1562179 | |||
| 2f1a9af5bd | |||
| 78c90734b2 | |||
| 7c246dcecd | |||
| f6252f62c4 | |||
| 9b91472186 | |||
| 3ff092fbda | |||
| d0b73977a6 | |||
| 823f082396 | |||
| 15701ef073 | |||
| 51c4bcc10b | |||
| 48ff2a347e | |||
| 922e61a636 | |||
| de0b95a22b | |||
| 47fe825aa7 | |||
| f7e47293dd | |||
| 80f05c2b74 | |||
| 8ec37f4337 | |||
| 2881d9e787 | |||
| b5f12cd3b4 | |||
| 8bd82ed409 | |||
| ff589abd2c | |||
| 121671e381 | |||
| a87e73d221 | |||
| 19ebd117bc | |||
| b03ab1aa35 | |||
| 7cb177cb7c | |||
| fb3aa3d793 | |||
| 5e79baa232 | |||
| 391bf568cb | |||
| ebc35741c9 | |||
| 7859ad9895 | |||
| 0ac17ac27f | |||
| 41444bb292 | |||
| 4ab4fbd599 | |||
| f97cfd58c0 | |||
| f0672f5dec | |||
| 2a0d01c161 | |||
| a7277be2ba | |||
| 7c0c9b986d | |||
| 98acb5ea70 | |||
| d65f944312 | |||
| ec6fe109f4 | |||
| 03d735a287 | |||
| d578feec18 | |||
| 13db4fc9e1 | |||
| 8760ad3904 | |||
| 81b9c57de7 | |||
| 652cad9726 | |||
| b031b4e971 | |||
| eed4a584e3 | |||
| 05693d090d | |||
| b0d17b0466 | |||
| f1acb787f5 | |||
| 289811e0e6 | |||
| a258d3c4ae | |||
| a03c11ae9d | |||
| 9e057ff815 | |||
| 5745161142 | |||
| 553fa097b4 | |||
| 8a3eb61a34 | |||
| 41e8164c2e | |||
| 26652160df | |||
| ece98887da | |||
| 7d07e18853 | |||
| 663a6c7f5c | |||
| 8be3cc9e78 | |||
| 74a0c8923c | |||
| 947d6dc6d1 | |||
| 288dacf613 | |||
| 546d65ab16 | |||
| 1b70807824 | |||
| 48da4994b9 | |||
| 5d51ac562a | |||
| 8e1ae18de0 | |||
| d723c64d5b | |||
| 67e09bbf66 | |||
| 5f898ed93a | |||
| 4621ed9e67 | |||
| ce32d7eea8 | |||
| cb4296396f | |||
| a3ec9618ed | |||
| 155d24fafb | |||
| 2208e1dbe5 | |||
| ffd29eb268 | |||
| 08f4a0877b | |||
| 827615f272 | |||
| a81f05dcc7 | |||
| 8e2904c541 | |||
| 1346d565c4 | |||
| ab61372e33 | |||
| 6709f0c653 | |||
| a88ef53104 | |||
| fad9428656 | |||
| c9a6f8b99b | |||
| c1c03a3f05 | |||
| e65227c7bf | |||
| 0206947202 | |||
| f1be4eaf5f | |||
| fd3fc0ea03 | |||
| ad7463c2c9 | |||
| 1008ed9f95 | |||
| d7379e897b | |||
| 20e546e5d3 | |||
| d6ff81bf87 | |||
| 8eecabb897 | |||
| c940976f7f | |||
| 2f1f266ea3 | |||
| 650f4706a8 | |||
| f41bf281fe | |||
| f7fd880388 | |||
| f67ebce739 | |||
| 79fd83449a | |||
| ed61419a6a | |||
| 971c4dcf9e | |||
| 2ee34b8e7c | |||
| 464f70f501 | |||
| a6fd6a5e69 | |||
| d3958dab42 | |||
| 5f7f0e4456 | |||
| 54ee6cf0c0 | |||
| 6a8b01425d | |||
| 751c976f95 | |||
| 854f1e5109 | |||
| 1e99374de0 | |||
| a700658132 | |||
| 84b2fc32b2 | |||
| b4c7ecbf9e | |||
| 9ad11b16d1 | |||
| 2d1785f513 | |||
| ea67a69351 | |||
| 2c1300acb4 | |||
| 0c2aa9ca5e | |||
| 6a6e449e49 | |||
| 2527d709fa | |||
| 1bb62fd134 |
@@ -77,7 +77,7 @@ namespace IRaCIS.Core.Application.Service.BusinessFilter
|
|||||||
else if(statusCode != 200&&!(objectResult.Value is IResponseOutput))
|
else if(statusCode != 200&&!(objectResult.Value is IResponseOutput))
|
||||||
{
|
{
|
||||||
//---程序错误,请联系开发人员。
|
//---程序错误,请联系开发人员。
|
||||||
var apiResponse = ResponseOutput.NotOk(StaticData.International("UnifiedAPI_ProgramError"));
|
var apiResponse = ResponseOutput.NotOk(I18n.T("UnifiedAPI_ProgramError"));
|
||||||
|
|
||||||
objectResult.Value = apiResponse;
|
objectResult.Value = apiResponse;
|
||||||
objectResult.DeclaredType = apiResponse.GetType();
|
objectResult.DeclaredType = apiResponse.GetType();
|
||||||
|
|||||||
@@ -10,25 +10,25 @@
|
|||||||
<PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.1.4" />
|
<PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.1.4" />
|
||||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="10.0.0" />
|
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="10.0.0" />
|
||||||
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" />
|
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" />
|
||||||
<PackageReference Include="AWSSDK.S3" Version="3.7.402.7" />
|
<PackageReference Include="AWSSDK.S3" Version="3.7.405" />
|
||||||
<PackageReference Include="AWSSDK.SecurityToken" Version="3.7.400.16" />
|
<PackageReference Include="AWSSDK.SecurityToken" Version="3.7.400.36" />
|
||||||
<PackageReference Include="DistributedLock.Core" Version="1.0.7" />
|
<PackageReference Include="DistributedLock.Core" Version="1.0.7" />
|
||||||
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.5" />
|
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.5" />
|
||||||
<PackageReference Include="fo-dicom" Version="5.1.3" />
|
<PackageReference Include="fo-dicom" Version="5.1.3" />
|
||||||
<PackageReference Include="fo-dicom.Codecs" Version="5.14.5" />
|
<PackageReference Include="fo-dicom.Codecs" Version="5.15.1" />
|
||||||
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.1.3" />
|
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.1.3" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.8" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.10" />
|
||||||
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
||||||
<PackageReference Include="Minio" Version="6.0.3" />
|
<PackageReference Include="Minio" Version="6.0.3" />
|
||||||
<PackageReference Include="My.Extensions.Localization.Json" Version="3.3.0">
|
<PackageReference Include="My.Extensions.Localization.Json" Version="3.3.0">
|
||||||
<TreatAsUsed>true</TreatAsUsed>
|
<TreatAsUsed>true</TreatAsUsed>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Panda.DynamicWebApi" Version="1.2.2" />
|
<PackageReference Include="Panda.DynamicWebApi" Version="1.2.2" />
|
||||||
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.1.1" />
|
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.1.2" />
|
||||||
<PackageReference Include="Serilog.Extensions.Hosting" Version="8.0.0" />
|
<PackageReference Include="Serilog.Extensions.Hosting" Version="8.0.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.7.3" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.9.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -315,7 +315,7 @@ namespace IRaCIS.Core.SCP.Service
|
|||||||
_SCPStudyIdList.Add(scpStudyId);
|
_SCPStudyIdList.Add(scpStudyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
var series = await _seriesRepository.FindAsync(seriesId);
|
var series = await _seriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId);
|
||||||
|
|
||||||
//没有缩略图
|
//没有缩略图
|
||||||
if (series != null && string.IsNullOrEmpty(series.ImageResizePath))
|
if (series != null && string.IsNullOrEmpty(series.ImageResizePath))
|
||||||
|
|||||||
@@ -75,9 +75,9 @@ namespace IRaCIS.Core.SCP.Service
|
|||||||
//using (@lock.Acquire())
|
//using (@lock.Acquire())
|
||||||
{
|
{
|
||||||
var findPatient = await _patientRepository.FirstOrDefaultAsync(t => t.PatientIdStr == patientIdStr && t.TrialSiteId==trialSiteId );
|
var findPatient = await _patientRepository.FirstOrDefaultAsync(t => t.PatientIdStr == patientIdStr && t.TrialSiteId==trialSiteId );
|
||||||
var findStudy = await _studyRepository.FindAsync(studyId);
|
var findStudy = await _studyRepository.FirstOrDefaultAsync(t=>t.Id== studyId);
|
||||||
var findSerice = await _seriesRepository.FindAsync(seriesId);
|
var findSerice = await _seriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId);
|
||||||
var findInstance = await _instanceRepository.FindAsync(instanceId);
|
var findInstance = await _instanceRepository.FirstOrDefaultAsync(t => t.Id == instanceId);
|
||||||
|
|
||||||
DateTime? studyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue<DateTime>(DicomTag.StudyDate).Add(dataset.GetSingleValueOrDefault(DicomTag.StudyTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue<DateTime>(DicomTag.StudyTime).TimeOfDay);
|
DateTime? studyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue<DateTime>(DicomTag.StudyDate).Add(dataset.GetSingleValueOrDefault(DicomTag.StudyTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue<DateTime>(DicomTag.StudyTime).TimeOfDay);
|
||||||
|
|
||||||
@@ -307,7 +307,7 @@ namespace IRaCIS.Core.SCP.Service
|
|||||||
await _instanceRepository.BatchUpdateNoTrackingAsync(t => t.Id == instanceId, u => new SCPInstance() { Path = fileRelativePath,FileSize=fileSize });
|
await _instanceRepository.BatchUpdateNoTrackingAsync(t => t.Id == instanceId, u => new SCPInstance() { Path = fileRelativePath,FileSize=fileSize });
|
||||||
}
|
}
|
||||||
|
|
||||||
//await _studyRepository.SaveChangesAsync();
|
await _studyRepository.SaveChangesAsync();
|
||||||
|
|
||||||
return findStudy.Id;
|
return findStudy.Id;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,43 +1,35 @@
|
|||||||
using System;
|
using Amazon.Auth.AccessControlPolicy;
|
||||||
using System.Net.Http;
|
|
||||||
using IRaCIS.Application.Interfaces;
|
|
||||||
using IRaCIS.Application.Contracts;
|
|
||||||
using IRaCIS.Core.Application.Auth;
|
|
||||||
using IRaCIS.Core.Application.Filter;
|
|
||||||
using IRaCIS.Core.Domain.Share;
|
|
||||||
using IRaCIS.Core.Infrastructure.Extention;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using IRaCIS.Core.Application.Interfaces;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using IRaCIS.Core.Application.Service;
|
|
||||||
using IRaCIS.Core.Infra.EFCore;
|
|
||||||
using IRaCIS.Core.Domain.Models;
|
|
||||||
using IRaCIS.Core.Infrastructure;
|
|
||||||
using System.Linq;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using MassTransit;
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
|
||||||
using IRaCIS.Core.Application.Helper;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using IRaCIS.Core.Application.Contracts;
|
|
||||||
using LoginReturnDTO = IRaCIS.Application.Contracts.LoginReturnDTO;
|
|
||||||
using DocumentFormat.OpenXml.Spreadsheet;
|
|
||||||
using AutoMapper.QueryableExtensions;
|
|
||||||
using NetTopologySuite.Algorithm;
|
|
||||||
using ZiggyCreatures.Caching.Fusion;
|
|
||||||
using AlibabaCloud.SDK.Sts20150401;
|
|
||||||
using AlibabaCloud.SDK.Sts20150401.Models;
|
|
||||||
using Org.BouncyCastle.Tls;
|
|
||||||
using Amazon.SecurityToken.Model;
|
|
||||||
using Amazon.SecurityToken;
|
using Amazon.SecurityToken;
|
||||||
using Amazon;
|
using Azure.Core;
|
||||||
|
using IRaCIS.Application.Contracts;
|
||||||
|
using IRaCIS.Application.Interfaces;
|
||||||
|
using IRaCIS.Core.API.OAuth;
|
||||||
|
using IRaCIS.Core.Application.Auth;
|
||||||
|
using IRaCIS.Core.Application.Contracts;
|
||||||
|
using IRaCIS.Core.Application.Helper;
|
||||||
|
using IRaCIS.Core.Application.Service;
|
||||||
|
using IRaCIS.Core.Domain.Models;
|
||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
|
using IRaCIS.Core.Infrastructure.Extention;
|
||||||
|
using MassTransit;
|
||||||
|
using MassTransit.Futures.Contracts;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using RestSharp;
|
||||||
|
using RestSharp.Authenticators;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using ZiggyCreatures.Caching.Fusion;
|
||||||
using AssumeRoleRequest = Amazon.SecurityToken.Model.AssumeRoleRequest;
|
using AssumeRoleRequest = Amazon.SecurityToken.Model.AssumeRoleRequest;
|
||||||
using AutoMapper;
|
using LoginReturnDTO = IRaCIS.Application.Contracts.LoginReturnDTO;
|
||||||
|
|
||||||
namespace IRaCIS.Api.Controllers
|
namespace IRaCIS.Api.Controllers
|
||||||
{
|
{
|
||||||
@@ -45,7 +37,10 @@ namespace IRaCIS.Api.Controllers
|
|||||||
/// 医生基本信息 、工作信息 专业信息、审核状态
|
/// 医生基本信息 、工作信息 专业信息、审核状态
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ApiController, ApiExplorerSettings(GroupName = "Reviewer")]
|
[ApiController, ApiExplorerSettings(GroupName = "Reviewer")]
|
||||||
public class ExtraController : ControllerBase
|
public class ExtraController([FromServices] IAttachmentService attachmentService, [FromServices] IDoctorService _doctorService,
|
||||||
|
[FromServices] IEducationService _educationService, [FromServices] ITrialExperienceService _trialExperienceService,
|
||||||
|
|
||||||
|
[FromServices] IResearchPublicationService _researchPublicationService, [FromServices] IVacationService _vacationService) : ControllerBase
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
@@ -60,34 +55,40 @@ namespace IRaCIS.Api.Controllers
|
|||||||
/// <param name="_vacationService"></param>
|
/// <param name="_vacationService"></param>
|
||||||
/// <param name="doctorId"></param>
|
/// <param name="doctorId"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpGet, Route("doctor/getDetail/{doctorId:guid}")]
|
[HttpPost, Route("doctor/getDetail")]
|
||||||
|
|
||||||
public async Task<IResponseOutput<DoctorDetailDTO>> GetDoctorDetail([FromServices] IAttachmentService attachmentService, [FromServices] IDoctorService _doctorService,
|
public async Task<IResponseOutput<DoctorDetailDTO>> GetDoctorDetail(GetDoctorDetailInDto inDto)
|
||||||
[FromServices] IEducationService _educationService, [FromServices] ITrialExperienceService _trialExperienceService,
|
|
||||||
|
|
||||||
[FromServices] IResearchPublicationService _researchPublicationService, [FromServices] IVacationService _vacationService, Guid doctorId)
|
|
||||||
{
|
{
|
||||||
var education = await _educationService.GetEducation(doctorId);
|
var education = await _educationService.GetEducation(inDto.doctorId);
|
||||||
|
|
||||||
var sowList = _doctorService.GetDoctorSowList(doctorId);
|
var sowList = _doctorService.GetDoctorSowList(inDto.doctorId);
|
||||||
var ackSowList = _doctorService.GetDoctorAckSowList(doctorId);
|
var ackSowList = _doctorService.GetDoctorAckSowList(inDto.doctorId);
|
||||||
|
|
||||||
var doctorDetail = new DoctorDetailDTO
|
var doctorDetail = new DoctorDetailDTO
|
||||||
{
|
{
|
||||||
AuditView = await _doctorService.GetAuditState(doctorId),
|
AuditView = await _doctorService.GetAuditState(inDto.doctorId),
|
||||||
BasicInfoView = await _doctorService.GetBasicInfo(doctorId),
|
BasicInfoView = await _doctorService.GetBasicInfo(inDto.doctorId),
|
||||||
EmploymentView = await _doctorService.GetEmploymentInfo(doctorId),
|
EmploymentView = await _doctorService.GetEmploymentInfo(inDto.doctorId),
|
||||||
AttachmentList = await attachmentService.GetAttachments(doctorId),
|
AttachmentList = await attachmentService.GetAttachments(inDto.doctorId),
|
||||||
|
SummarizeInfo = await _doctorService.GetSummarizeInfo(new GetSummarizeInfoInDto()
|
||||||
|
{
|
||||||
|
DoctorId = inDto.doctorId,
|
||||||
|
TrialId = inDto.TrialId
|
||||||
|
}),
|
||||||
|
PaymentModeInfo = await _doctorService.GetPaymentMode(inDto.doctorId),
|
||||||
EducationList = education.EducationList,
|
EducationList = education.EducationList,
|
||||||
PostgraduateList = education.PostgraduateList,
|
PostgraduateList = education.PostgraduateList,
|
||||||
|
|
||||||
TrialExperienceView = await _trialExperienceService.GetTrialExperience(doctorId),
|
TrialExperienceView = await _trialExperienceService.GetTrialExperience(new TrialExperienceModelIndto()
|
||||||
ResearchPublicationView = await _researchPublicationService.GetResearchPublication(doctorId),
|
{
|
||||||
|
DoctorId = inDto.doctorId,
|
||||||
|
TrialId = inDto.TrialId
|
||||||
|
}),
|
||||||
|
ResearchPublicationView = await _researchPublicationService.GetResearchPublication(inDto.doctorId),
|
||||||
|
|
||||||
SpecialtyView = await _doctorService.GetSpecialtyInfo(doctorId),
|
SpecialtyView = await _doctorService.GetSpecialtyInfo(inDto.doctorId),
|
||||||
InHoliday = (await _vacationService.OnVacation(doctorId)).IsSuccess,
|
InHoliday = (await _vacationService.OnVacation(inDto.doctorId)).IsSuccess,
|
||||||
IntoGroupInfo = _doctorService.GetDoctorIntoGroupInfo(doctorId),
|
IntoGroupInfo = _doctorService.GetDoctorIntoGroupInfo(inDto.doctorId),
|
||||||
SowList = sowList,
|
SowList = sowList,
|
||||||
AckSowList = ackSowList
|
AckSowList = ackSowList
|
||||||
};
|
};
|
||||||
@@ -371,49 +372,6 @@ namespace IRaCIS.Api.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#region aliyun-net-sdk-sts 之前
|
|
||||||
//[HttpGet("user/GenerateSTS")]
|
|
||||||
//public IResponseOutput GenerateSTS([FromServices] IOptionsMonitor<AliyunOSSOptions> options)
|
|
||||||
//{
|
|
||||||
// var ossOptions = options.CurrentValue;
|
|
||||||
|
|
||||||
|
|
||||||
// IClientProfile profile = DefaultProfile.GetProfile(ossOptions.regionId, ossOptions.accessKeyId, ossOptions.accessKeySecret);
|
|
||||||
// DefaultAcsClient client = new DefaultAcsClient(profile);
|
|
||||||
|
|
||||||
|
|
||||||
// // 创建一个STS请求
|
|
||||||
// AssumeRoleRequest request = new AssumeRoleRequest
|
|
||||||
// {
|
|
||||||
// RoleArn = ossOptions.roleArn, // 角色ARN,需要替换为你的角色ARN
|
|
||||||
// RoleSessionName = $"session-name-{NewId.NextGuid()}", // 角色会话名称,可自定义
|
|
||||||
// DurationSeconds = 900, // 令牌有效期(单位:秒),这里设置为1小时
|
|
||||||
// };
|
|
||||||
|
|
||||||
|
|
||||||
// AssumeRoleResponse response = client.GetAcsResponse(request);
|
|
||||||
|
|
||||||
// // 返回STS令牌信息给前端
|
|
||||||
// var stsToken = new
|
|
||||||
// {
|
|
||||||
// AccessKeyId = response.Credentials.AccessKeyId,
|
|
||||||
// AccessKeySecret = response.Credentials.AccessKeySecret,
|
|
||||||
// SecurityToken = response.Credentials.SecurityToken,
|
|
||||||
// Expiration = response.Credentials.Expiration,
|
|
||||||
|
|
||||||
// Region = ossOptions.region,
|
|
||||||
// BucketName = ossOptions.bucketName,
|
|
||||||
// ViewEndpoint = ossOptions.viewEndpoint,
|
|
||||||
|
|
||||||
// };
|
|
||||||
|
|
||||||
// return ResponseOutput.Ok(stsToken);
|
|
||||||
|
|
||||||
//}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[HttpGet("User/UserRedirect")]
|
[HttpGet("User/UserRedirect")]
|
||||||
@@ -445,7 +403,104 @@ namespace IRaCIS.Api.Controllers
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region 项目支持Oauth 对接修改
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 回调到前端,前端调用后端的接口
|
||||||
|
/// 参考链接:https://www.ruanyifeng.com/blog/2019/04/oauth-grant-types.html
|
||||||
|
/// 后端通过这个code ,带上客户端信息,和授权类型 可以向单点登录提供商,获取厂商token
|
||||||
|
///
|
||||||
|
/// 但是单点登录提供商提供的token 和我们系统的token 是有区别的,我们的token里面有我们业务系统的UserId,涉及到很多业务操作,所以在此出现了两种方案
|
||||||
|
/// 1、前端使用厂商的Token。 后端通过code 获取厂商的Token 返回前端的同时返回我们系统的UserId,前段在http 请求头加上一个自定义参数,带上UserId 后端取用户Id的地方变动下,
|
||||||
|
/// 但是除了UserId外,后端还有其他信息也是从Token取的,所以在请求头也需要带上,此外后端认证Token的方式也需要变化,改造成本稍大(如果是微服务,做这种处理还是可以的)。
|
||||||
|
/// 2、前端还是使用我们后台自己的Token。后端通过code 获取厂商Token的同时,后端做一个隐藏登录,返回厂商的Token的同时,也返回我们系统的Token。
|
||||||
|
/// (像我们单体,这种方式最简单,我们用单点登录,无非就是不想记多个系统的密码,自动登录而已,其他不支持的项目改造成本也是最低的)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">回调的厂商类型 比如github, google, 我们用的logto ,不同的厂商回调到前端的地址可以不同的,但是请求后端的接口可以是同一个 </param>
|
||||||
|
/// <param name="code">在第三方平台登录成功后,回调前端的时候会返回一个code </param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpGet("User/OAuthCallBack")]
|
||||||
|
public async Task<IResponseOutput> OAuthCallBack(string type, string code)
|
||||||
|
{
|
||||||
|
#region 获取AccessTo
|
||||||
|
|
||||||
|
//var headerDic = new Dictionary<string, string>();
|
||||||
|
//headerDic.Add("code", code);
|
||||||
|
//headerDic.Add("grant_type", "authorization_code");
|
||||||
|
//headerDic.Add("redirect_uri", "http://localhost:6100");
|
||||||
|
//headerDic.Add("scope", "all");
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region 客户端方式获取logto 里面的信息
|
||||||
|
|
||||||
|
var baseUrl = "https://logto.test.extimaging.com";
|
||||||
|
var appId = "v2mr2ndxwkxz0xpsuc1th";
|
||||||
|
var appSecret = "yq9jUxl70QoOmwHxJ37h1rDoyJ5iz92Q";
|
||||||
|
var apiAddress = "https://default.logto.app/api"; //这里是个坑
|
||||||
|
var scope = "all";
|
||||||
|
|
||||||
|
var opts = new RestClientOptions(baseUrl);
|
||||||
|
using var client = new RestClient(opts);
|
||||||
|
|
||||||
|
//https://bump.sh/logto/doc/logto-management-api/authentication
|
||||||
|
var request = new RestRequest("oidc/token", Method.Post);
|
||||||
|
request
|
||||||
|
.AddHeader("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
.AddParameter("grant_type", "client_credentials")
|
||||||
|
.AddParameter("client_id", appId)
|
||||||
|
.AddParameter("client_secret", appSecret)
|
||||||
|
.AddParameter("resource", apiAddress) //注意这里默认值地址和api 地址有区别
|
||||||
|
.AddParameter("scope", scope);
|
||||||
|
|
||||||
|
|
||||||
|
var response = await client.ExecuteAsync<LogtoTokenResponse>(request);
|
||||||
|
|
||||||
|
if (response.StatusCode == HttpStatusCode.OK)
|
||||||
|
{
|
||||||
|
var tokenResponse = response.Data;
|
||||||
|
|
||||||
|
Console.WriteLine(tokenResponse.ToJsonStr());
|
||||||
|
|
||||||
|
#region 获取应用信息
|
||||||
|
|
||||||
|
var applicationRequest = new RestRequest($"/api/applications", Method.Get)
|
||||||
|
.AddHeader("Authorization", $"Bearer {tokenResponse.AccessToken}");
|
||||||
|
|
||||||
|
var applicationResponse = await client.ExecuteAsync(applicationRequest);
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 获取用户信息
|
||||||
|
//curl \
|
||||||
|
// -X GET https://[tenant_id].logto.app/api/users/{userId} \
|
||||||
|
// -H "Authorization: Bearer $ACCESS_TOKEN"
|
||||||
|
|
||||||
|
var userId = "4fqx4cb3438k";
|
||||||
|
var userInfoRequest = new RestRequest($"api/users/{userId}", Method.Get)
|
||||||
|
.AddHeader("Authorization", $"Bearer {tokenResponse.AccessToken}");
|
||||||
|
|
||||||
|
|
||||||
|
var userResponse = await client.ExecuteAsync<LogtoUser>(userInfoRequest);
|
||||||
|
|
||||||
|
Console.WriteLine(userResponse.Content);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
return ResponseOutput.Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 测试获取用户 ip
|
||||||
[HttpGet, Route("ip")]
|
[HttpGet, Route("ip")]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
public IResponseOutput Get([FromServices] IHttpContextAccessor _context)
|
public IResponseOutput Get([FromServices] IHttpContextAccessor _context)
|
||||||
@@ -486,6 +541,9 @@ namespace IRaCIS.Api.Controllers
|
|||||||
}
|
}
|
||||||
return ResponseOutput.Ok(sb.ToString());
|
return ResponseOutput.Ok(sb.ToString());
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using IRaCIS.Application.Contracts;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using IRaCIS.Application.Interfaces;
|
using IRaCIS.Application.Interfaces;
|
||||||
using IRaCIS.Application.Contracts;
|
using IRaCIS.Core.Application.BusinessFilter;
|
||||||
using IRaCIS.Core.Application.Filter;
|
using IRaCIS.Core.Application.Filter;
|
||||||
using IRaCIS.Core.Infrastructure.Extention;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using IRaCIS.Core.Application.Service;
|
using IRaCIS.Core.Application.Service;
|
||||||
using IRaCIS.Core.Application.Service.Inspection.DTO;
|
using IRaCIS.Core.Application.Service.Inspection.DTO;
|
||||||
using IRaCIS.Core.Infra.EFCore;
|
|
||||||
using IRaCIS.Core.Domain.Models;
|
using IRaCIS.Core.Domain.Models;
|
||||||
using IRaCIS.Core.Application.Auth;
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
|
using IRaCIS.Core.Infrastructure.Extention;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace IRaCIS.Core.API.Controllers.Special
|
namespace IRaCIS.Core.API.Controllers.Special
|
||||||
{
|
{
|
||||||
@@ -48,7 +48,7 @@ namespace IRaCIS.Core.API.Controllers.Special
|
|||||||
[HttpPost, Route("trial/addOrUpdateTrial")]
|
[HttpPost, Route("trial/addOrUpdateTrial")]
|
||||||
//[Authorize(Policy = IRaCISPolicy.PM_APM)]
|
//[Authorize(Policy = IRaCISPolicy.PM_APM)]
|
||||||
|
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AddOrUpdateTrial", "BeforeOngoingCantOpt", "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit( "AddOrUpdateTrial", "BeforeOngoingCantOpt", "AfterStopCannNotOpt" )]
|
||||||
public async Task<IResponseOutput<Trial>> AddOrUpdateTrial(TrialCommand param)
|
public async Task<IResponseOutput<Trial>> AddOrUpdateTrial(TrialCommand param)
|
||||||
{
|
{
|
||||||
var userId = Guid.Parse(User.FindFirst("id").Value);
|
var userId = Guid.Parse(User.FindFirst("id").Value);
|
||||||
@@ -88,7 +88,7 @@ namespace IRaCIS.Core.API.Controllers.Special
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
|
|
||||||
[HttpPost, Route("doctorWorkload/workLoadAddOrUpdate")]
|
[HttpPost, Route("doctorWorkload/workLoadAddOrUpdate")]
|
||||||
[TypeFilter(typeof(TrialResourceFilter),Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
||||||
public async Task<IResponseOutput> WorkLoadAddOrUpdate([FromServices] IDoctorWorkloadService _trialWorkloadService, WorkloadCommand workLoadAddOrUpdateModel)
|
public async Task<IResponseOutput> WorkLoadAddOrUpdate([FromServices] IDoctorWorkloadService _trialWorkloadService, WorkloadCommand workLoadAddOrUpdateModel)
|
||||||
{
|
{
|
||||||
var userId = Guid.Parse(User.FindFirst("id").Value);
|
var userId = Guid.Parse(User.FindFirst("id").Value);
|
||||||
@@ -110,7 +110,7 @@ namespace IRaCIS.Core.API.Controllers.Special
|
|||||||
|
|
||||||
|
|
||||||
[HttpDelete, Route("doctorWorkload/deleteWorkLoad/{id:guid}/{trialId:guid}")]
|
[HttpDelete, Route("doctorWorkload/deleteWorkLoad/{id:guid}/{trialId:guid}")]
|
||||||
[TypeFilter(typeof(TrialResourceFilter),Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
||||||
public async Task<IResponseOutput> DeleteWorkLoad([FromServices] IDoctorWorkloadService _trialWorkloadService, Guid id)
|
public async Task<IResponseOutput> DeleteWorkLoad([FromServices] IDoctorWorkloadService _trialWorkloadService, Guid id)
|
||||||
{
|
{
|
||||||
//先判断该工作量的费用是否被锁定,如果被锁定,则不能删除
|
//先判断该工作量的费用是否被锁定,如果被锁定,则不能删除
|
||||||
|
|||||||
@@ -1,24 +1,19 @@
|
|||||||
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using AutoMapper;
|
|
||||||
|
|
||||||
using IRaCIS.Application.Interfaces;
|
using IRaCIS.Application.Interfaces;
|
||||||
|
using IRaCIS.Core.Application.BusinessFilter;
|
||||||
using IRaCIS.Core.Application.Contracts;
|
using IRaCIS.Core.Application.Contracts;
|
||||||
using IRaCIS.Core.Application.Filter;
|
using IRaCIS.Core.Application.Filter;
|
||||||
using IRaCIS.Core.Application.Image.QA;
|
using IRaCIS.Core.Application.Image.QA;
|
||||||
using IRaCIS.Core.Application.Interfaces;
|
|
||||||
using IRaCIS.Core.Application.Service;
|
using IRaCIS.Core.Application.Service;
|
||||||
using IRaCIS.Core.Application.Service.Inspection.DTO;
|
using IRaCIS.Core.Application.Service.Inspection.DTO;
|
||||||
using IRaCIS.Core.Application.Service.Inspection.Interface;
|
using IRaCIS.Core.Application.Service.Inspection.Interface;
|
||||||
using IRaCIS.Core.Application.Service.Reading.Dto;
|
using IRaCIS.Core.Application.Service.Reading.Dto;
|
||||||
using IRaCIS.Core.Application.Service.Reading.Interface;
|
using IRaCIS.Core.Application.Service.Reading.Interface;
|
||||||
using IRaCIS.Core.Application.ViewModel;
|
using IRaCIS.Core.Application.ViewModel;
|
||||||
using IRaCIS.Core.Domain.Models;
|
|
||||||
using IRaCIS.Core.Domain.Share;
|
|
||||||
using IRaCIS.Core.Infra.EFCore;
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
using IRaCIS.Core.Infrastructure.Extention;
|
using IRaCIS.Core.Infrastructure.Extention;
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.API.Controllers
|
namespace IRaCIS.Core.API.Controllers
|
||||||
@@ -57,7 +52,7 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
/// <param name="opt"></param>
|
/// <param name="opt"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/ReadingImageTask/SubmitOncologyReadingInfo")]
|
[HttpPost, Route("Inspection/ReadingImageTask/SubmitOncologyReadingInfo")]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
||||||
|
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> SetOncologyReadingInfo(DataInspectionDto<SubmitOncologyReadingInfoInDto> opt)
|
public async Task<IResponseOutput> SetOncologyReadingInfo(DataInspectionDto<SubmitOncologyReadingInfoInDto> opt)
|
||||||
@@ -74,7 +69,7 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
/// <param name="opt"></param>
|
/// <param name="opt"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/ReadingImageTask/SubmitDicomVisitTask")]
|
[HttpPost, Route("Inspection/ReadingImageTask/SubmitDicomVisitTask")]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
||||||
|
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> SubmitDicomVisitTask(DataInspectionDto<SubmitDicomVisitTaskInDto> opt)
|
public async Task<IResponseOutput> SubmitDicomVisitTask(DataInspectionDto<SubmitDicomVisitTaskInDto> opt)
|
||||||
@@ -93,7 +88,7 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
/// <param name="opt"></param>
|
/// <param name="opt"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/ReadingImageTask/SubmitGlobalReadingInfo")]
|
[HttpPost, Route("Inspection/ReadingImageTask/SubmitGlobalReadingInfo")]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
||||||
|
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> SubmitGlobalReadingInfo(DataInspectionDto<SubmitGlobalReadingInfoInDto> opt)
|
public async Task<IResponseOutput> SubmitGlobalReadingInfo(DataInspectionDto<SubmitGlobalReadingInfoInDto> opt)
|
||||||
@@ -112,7 +107,7 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
/// <param name="opt"></param>
|
/// <param name="opt"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/configTrialBasicInfo/TrialReadingInfoSign")]
|
[HttpPost, Route("Inspection/configTrialBasicInfo/TrialReadingInfoSign")]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
||||||
|
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> TrialReadingInfoSign(DataInspectionDto<TrialReadingInfoSignInDto> opt)
|
public async Task<IResponseOutput> TrialReadingInfoSign(DataInspectionDto<TrialReadingInfoSignInDto> opt)
|
||||||
@@ -131,7 +126,7 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
/// <param name="opt"></param>
|
/// <param name="opt"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/ReadingMedicalReview/FinishMedicalReview")]
|
[HttpPost, Route("Inspection/ReadingMedicalReview/FinishMedicalReview")]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
||||||
|
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> FinishMedicalReview(DataInspectionDto<FinishMedicalReviewInDto> opt)
|
public async Task<IResponseOutput> FinishMedicalReview(DataInspectionDto<FinishMedicalReviewInDto> opt)
|
||||||
@@ -148,7 +143,7 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
/// <param name="opt"></param>
|
/// <param name="opt"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/ReadingMedicineQuestion/ConfirmReadingMedicineQuestion")]
|
[HttpPost, Route("Inspection/ReadingMedicineQuestion/ConfirmReadingMedicineQuestion")]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
||||||
|
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> ConfirmReadingMedicineQuestion(DataInspectionDto<ConfirmReadingMedicineQuestionInDto> opt)
|
public async Task<IResponseOutput> ConfirmReadingMedicineQuestion(DataInspectionDto<ConfirmReadingMedicineQuestionInDto> opt)
|
||||||
@@ -166,7 +161,7 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
/// <param name="opt"></param>
|
/// <param name="opt"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/ReadingImageTask/SubmitVisitTaskQuestions")]
|
[HttpPost, Route("Inspection/ReadingImageTask/SubmitVisitTaskQuestions")]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
||||||
|
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> SubmitVisitTaskQuestions(DataInspectionDto<SubmitVisitTaskQuestionsInDto> opt)
|
public async Task<IResponseOutput> SubmitVisitTaskQuestions(DataInspectionDto<SubmitVisitTaskQuestionsInDto> opt)
|
||||||
@@ -184,7 +179,7 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
/// <param name="opt"></param>
|
/// <param name="opt"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/ClinicalAnswer/CRCSignClinicalData")]
|
[HttpPost, Route("Inspection/ClinicalAnswer/CRCSignClinicalData")]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
||||||
|
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> CRCSignClinicalData(DataInspectionDto<CRCSignClinicalDataInDto> opt)
|
public async Task<IResponseOutput> CRCSignClinicalData(DataInspectionDto<CRCSignClinicalDataInDto> opt)
|
||||||
@@ -202,7 +197,7 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
/// <param name="opt"></param>
|
/// <param name="opt"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/ClinicalAnswer/CRCConfirmClinical")]
|
[HttpPost, Route("Inspection/ClinicalAnswer/CRCConfirmClinical")]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
||||||
|
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> CRCConfirmClinical(DataInspectionDto<CRCConfirmClinicalInDto> opt)
|
public async Task<IResponseOutput> CRCConfirmClinical(DataInspectionDto<CRCConfirmClinicalInDto> opt)
|
||||||
@@ -219,7 +214,7 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
/// <param name="opt"></param>
|
/// <param name="opt"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/ClinicalAnswer/CRCCancelConfirmClinical")]
|
[HttpPost, Route("Inspection/ClinicalAnswer/CRCCancelConfirmClinical")]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
||||||
|
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> CRCCancelConfirmClinical(DataInspectionDto<CRCCancelConfirmClinicalInDto> opt)
|
public async Task<IResponseOutput> CRCCancelConfirmClinical(DataInspectionDto<CRCCancelConfirmClinicalInDto> opt)
|
||||||
@@ -237,7 +232,7 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
/// <param name="opt"></param>
|
/// <param name="opt"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/ClinicalAnswer/PMConfirmClinical")]
|
[HttpPost, Route("Inspection/ClinicalAnswer/PMConfirmClinical")]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
||||||
|
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> PMConfirmClinical(DataInspectionDto<CRCConfirmClinicalInDto> opt)
|
public async Task<IResponseOutput> PMConfirmClinical(DataInspectionDto<CRCConfirmClinicalInDto> opt)
|
||||||
@@ -255,7 +250,7 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
/// <param name="opt"></param>
|
/// <param name="opt"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/ReadingClinicalData/SignConsistencyAnalysisReadingClinicalData")]
|
[HttpPost, Route("Inspection/ReadingClinicalData/SignConsistencyAnalysisReadingClinicalData")]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
||||||
|
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> SignConsistencyAnalysisReadingClinicalData(DataInspectionDto<SignConsistencyAnalysisReadingClinicalDataInDto> opt)
|
public async Task<IResponseOutput> SignConsistencyAnalysisReadingClinicalData(DataInspectionDto<SignConsistencyAnalysisReadingClinicalDataInDto> opt)
|
||||||
@@ -272,7 +267,7 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
/// <param name="opt"></param>
|
/// <param name="opt"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/ClinicalAnswer/SubmitClinicalFormAndSign")]
|
[HttpPost, Route("Inspection/ClinicalAnswer/SubmitClinicalFormAndSign")]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
||||||
|
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> SubmitClinicalFormAndSign(DataInspectionDto<SubmitClinicalFormInDto> opt)
|
public async Task<IResponseOutput> SubmitClinicalFormAndSign(DataInspectionDto<SubmitClinicalFormInDto> opt)
|
||||||
@@ -289,7 +284,7 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
/// <param name="opt"></param>
|
/// <param name="opt"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/ReadingImageTask/SubmitJudgeVisitTaskResult")]
|
[HttpPost, Route("Inspection/ReadingImageTask/SubmitJudgeVisitTaskResult")]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
||||||
|
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> SubmitJudgeVisitTaskResult(DataInspectionDto<SaveJudgeVisitTaskResult> opt)
|
public async Task<IResponseOutput> SubmitJudgeVisitTaskResult(DataInspectionDto<SaveJudgeVisitTaskResult> opt)
|
||||||
@@ -308,7 +303,7 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/configTrialBasicInfo/ConfigTrialBasicInfoConfirm")]
|
[HttpPost, Route("Inspection/configTrialBasicInfo/ConfigTrialBasicInfoConfirm")]
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt" })]
|
[TrialGlobalLimit( "BeforeOngoingCantOpt" )]
|
||||||
public async Task<IResponseOutput> ConfigTrialBasicInfoConfirm(DataInspectionDto<BasicTrialConfig> opt)
|
public async Task<IResponseOutput> ConfigTrialBasicInfoConfirm(DataInspectionDto<BasicTrialConfig> opt)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -328,7 +323,7 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/configTrialBasicInfo/ConfigTrialProcessInfoConfirm")]
|
[HttpPost, Route("Inspection/configTrialBasicInfo/ConfigTrialProcessInfoConfirm")]
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
//[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt" })]
|
//[TrialGlobalLimit( "BeforeOngoingCantOpt" )]
|
||||||
public async Task<IResponseOutput> ConfigTrialProcessInfoConfirm(DataInspectionDto<TrialProcessConfig> opt)
|
public async Task<IResponseOutput> ConfigTrialProcessInfoConfirm(DataInspectionDto<TrialProcessConfig> opt)
|
||||||
{
|
{
|
||||||
opt.Data.IsTrialProcessConfirmed = true;
|
opt.Data.IsTrialProcessConfirmed = true;
|
||||||
@@ -350,7 +345,7 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/configTrialBasicInfo/ConfigTrialUrgentInfoConfirm")]
|
[HttpPost, Route("Inspection/configTrialBasicInfo/ConfigTrialUrgentInfoConfirm")]
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt" })]
|
[TrialGlobalLimit( "BeforeOngoingCantOpt" )]
|
||||||
public async Task<IResponseOutput> ConfigTrialUrgentInfoConfirm(DataInspectionDto<TrialUrgentConfig> opt)
|
public async Task<IResponseOutput> ConfigTrialUrgentInfoConfirm(DataInspectionDto<TrialUrgentConfig> opt)
|
||||||
{
|
{
|
||||||
opt.Data.IsTrialUrgentConfirmed = true;
|
opt.Data.IsTrialUrgentConfirmed = true;
|
||||||
@@ -363,7 +358,7 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
|
|
||||||
[HttpPost, Route("Inspection/configTrialBasicInfo/ConfigTrialPACSInfoConfirm")]
|
[HttpPost, Route("Inspection/configTrialBasicInfo/ConfigTrialPACSInfoConfirm")]
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt" })]
|
[TrialGlobalLimit( "BeforeOngoingCantOpt" )]
|
||||||
public async Task<IResponseOutput> ConfigTrialPACSInfoConfirm(DataInspectionDto<TrialPACSConfig> opt)
|
public async Task<IResponseOutput> ConfigTrialPACSInfoConfirm(DataInspectionDto<TrialPACSConfig> opt)
|
||||||
{
|
{
|
||||||
opt.Data.IsTrialPACSConfirmed = true;
|
opt.Data.IsTrialPACSConfirmed = true;
|
||||||
@@ -379,7 +374,7 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/configTrialBasicInfo/TrialConfigSignatureConfirm")]
|
[HttpPost, Route("Inspection/configTrialBasicInfo/TrialConfigSignatureConfirm")]
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
[TypeFilter(typeof(TrialResourceFilter),Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
||||||
|
|
||||||
public async Task<IResponseOutput> TrialConfigSignatureConfirm(DataInspectionDto<SignConfirmDTO> opt)
|
public async Task<IResponseOutput> TrialConfigSignatureConfirm(DataInspectionDto<SignConfirmDTO> opt)
|
||||||
{
|
{
|
||||||
@@ -396,7 +391,7 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/ReadingCriterion/ResetAndAsyncCriterion")]
|
[HttpPost, Route("Inspection/ReadingCriterion/ResetAndAsyncCriterion")]
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
||||||
|
|
||||||
public async Task<IResponseOutput> ResetAndAsyncCriterion(DataInspectionDto<ResetAndAsyncCriterionInDto> opt)
|
public async Task<IResponseOutput> ResetAndAsyncCriterion(DataInspectionDto<ResetAndAsyncCriterionInDto> opt)
|
||||||
{
|
{
|
||||||
@@ -414,7 +409,7 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
/// <param name="opt"></param>
|
/// <param name="opt"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/QCOperation/CRCRequestToQC")]
|
[HttpPost, Route("Inspection/QCOperation/CRCRequestToQC")]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> CRCRequestToQC(DataInspectionDto<CRCRequestToQCCommand> opt)
|
public async Task<IResponseOutput> CRCRequestToQC(DataInspectionDto<CRCRequestToQCCommand> opt)
|
||||||
{
|
{
|
||||||
@@ -429,7 +424,7 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
/// 设置QC 通过或者不通过 7:QC failed 8:QC passed
|
/// 设置QC 通过或者不通过 7:QC failed 8:QC passed
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpPost, Route("Inspection/QCOperation/QCPassedOrFailed")]
|
[HttpPost, Route("Inspection/QCOperation/QCPassedOrFailed")]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> QCPassedOrFailed(DataInspectionDto<QCPassedOrFailedDto> opt)
|
public async Task<IResponseOutput> QCPassedOrFailed(DataInspectionDto<QCPassedOrFailedDto> opt)
|
||||||
{
|
{
|
||||||
@@ -443,7 +438,7 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
/// 一致性核查 回退 对话记录不清除 只允许PM回退
|
/// 一致性核查 回退 对话记录不清除 只允许PM回退
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpPost, Route("Inspection/QCOperation/CheckBack")]
|
[HttpPost, Route("Inspection/QCOperation/CheckBack")]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> CheckBack(DataInspectionDto<IDDto> opt)
|
public async Task<IResponseOutput> CheckBack(DataInspectionDto<IDDto> opt)
|
||||||
{
|
{
|
||||||
@@ -460,7 +455,7 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
/// <param name="opt"></param>
|
/// <param name="opt"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/ReadClinicalData/ReadClinicalDataSign")]
|
[HttpPost, Route("Inspection/ReadClinicalData/ReadClinicalDataSign")]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> ReadClinicalDataSign(DataInspectionDto<ReadingClinicalDataSignIndto> opt)
|
public async Task<IResponseOutput> ReadClinicalDataSign(DataInspectionDto<ReadingClinicalDataSignIndto> opt)
|
||||||
{
|
{
|
||||||
@@ -475,7 +470,7 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
/// CRC 设置已经重传完成
|
/// CRC 设置已经重传完成
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpPost, Route("Inspection/QCOperation/SetReuploadFinished")]
|
[HttpPost, Route("Inspection/QCOperation/SetReuploadFinished")]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> SetReuploadFinished(DataInspectionDto<CRCReuploadFinishedCommand> opt)
|
public async Task<IResponseOutput> SetReuploadFinished(DataInspectionDto<CRCReuploadFinishedCommand> opt)
|
||||||
{
|
{
|
||||||
@@ -491,8 +486,7 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
/// <param name="opt"></param>
|
/// <param name="opt"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/TrialConfig/updateTrialState")]
|
[HttpPost, Route("Inspection/TrialConfig/updateTrialState")]
|
||||||
//[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit( "BeforeOngoingCantOpt")]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt" })]
|
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> UpdateTrialState(DataInspectionDto<UpdateTrialStateDto> opt)
|
public async Task<IResponseOutput> UpdateTrialState(DataInspectionDto<UpdateTrialStateDto> opt)
|
||||||
{
|
{
|
||||||
@@ -508,7 +502,7 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/TrialDocument/userConfirm")]
|
[HttpPost, Route("Inspection/TrialDocument/userConfirm")]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt", "SignSystemDocNoTrialId", "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit( "BeforeOngoingCantOpt", "SignSystemDocNoTrialId", "AfterStopCannNotOpt" )]
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> UserConfirm(DataInspectionDto<UserConfirmCommand> opt)
|
public async Task<IResponseOutput> UserConfirm(DataInspectionDto<UserConfirmCommand> opt)
|
||||||
{
|
{
|
||||||
@@ -525,7 +519,7 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/VisitTask/ConfirmReReading")]
|
[HttpPost, Route("Inspection/VisitTask/ConfirmReReading")]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
|
|
||||||
public async Task<IResponseOutput> ConfirmReReading(DataInspectionDto<ConfirmReReadingCommand> opt, [FromServices] IVisitTaskHelpeService _visitTaskCommonService, [FromServices] IVisitTaskService _visitTaskService)
|
public async Task<IResponseOutput> ConfirmReReading(DataInspectionDto<ConfirmReReadingCommand> opt, [FromServices] IVisitTaskHelpeService _visitTaskCommonService, [FromServices] IVisitTaskService _visitTaskService)
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using DocumentFormat.OpenXml.Drawing;
|
|
||||||
using ExcelDataReader;
|
using ExcelDataReader;
|
||||||
using IRaCIS.Application.Contracts;
|
|
||||||
using IRaCIS.Application.Interfaces;
|
using IRaCIS.Application.Interfaces;
|
||||||
using IRaCIS.Core.Application.Auth;
|
using IRaCIS.Core.Application.BusinessFilter;
|
||||||
using IRaCIS.Core.Application.Contracts;
|
using IRaCIS.Core.Application.Contracts;
|
||||||
using IRaCIS.Core.Application.Contracts.Dicom;
|
using IRaCIS.Core.Application.Contracts.Dicom;
|
||||||
using IRaCIS.Core.Application.Contracts.Dicom.DTO;
|
using IRaCIS.Core.Application.Contracts.Dicom.DTO;
|
||||||
@@ -11,20 +9,17 @@ using IRaCIS.Core.Application.Filter;
|
|||||||
using IRaCIS.Core.Application.Helper;
|
using IRaCIS.Core.Application.Helper;
|
||||||
using IRaCIS.Core.Application.MassTransit.Command;
|
using IRaCIS.Core.Application.MassTransit.Command;
|
||||||
using IRaCIS.Core.Application.Service;
|
using IRaCIS.Core.Application.Service;
|
||||||
using IRaCIS.Core.Application.Service.ImageAndDoc;
|
|
||||||
using IRaCIS.Core.Application.Service.Reading.Dto;
|
|
||||||
using IRaCIS.Core.Domain.Models;
|
using IRaCIS.Core.Domain.Models;
|
||||||
using IRaCIS.Core.Domain.Share;
|
using IRaCIS.Core.Domain.Share;
|
||||||
using IRaCIS.Core.Infra.EFCore;
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
using IRaCIS.Core.Infrastructure;
|
using IRaCIS.Core.Infrastructure;
|
||||||
using IRaCIS.Core.Infrastructure.Extention;
|
using IRaCIS.Core.Infrastructure.Extention;
|
||||||
using MassTransit;
|
|
||||||
using MassTransit.Mediator;
|
using MassTransit.Mediator;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Cors.Infrastructure;
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNetCore.SignalR;
|
using Microsoft.AspNetCore.SignalR;
|
||||||
using Microsoft.AspNetCore.StaticFiles;
|
using Microsoft.AspNetCore.StaticFiles;
|
||||||
@@ -40,23 +35,46 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using System.Globalization;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using ZiggyCreatures.Caching.Fusion;
|
|
||||||
using Path = System.IO.Path;
|
using Path = System.IO.Path;
|
||||||
|
|
||||||
namespace IRaCIS.Core.API.Controllers
|
namespace IRaCIS.Core.API.Controllers
|
||||||
{
|
{
|
||||||
|
|
||||||
#region 上传基类封装
|
#region 上传基类封装
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
[DisableFormValueModelBinding]
|
[DisableFormValueModelBinding]
|
||||||
public abstract class UploadBaseController : ControllerBase
|
public abstract class UploadBaseController : ControllerBase
|
||||||
{
|
{
|
||||||
/// <summary> 流式上传 直接返回</summary>
|
/// <summary> 流式上传 直接返回</summary>
|
||||||
[Route("base")]
|
[Route("SingleFileUpload")]
|
||||||
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
|
|
||||||
public virtual async Task<IResponseOutput> SingleFileUploadAsync(Func<string, (string, string)> filePathFunc)
|
public virtual async Task<IResponseOutput> SingleFileUploadAsync(Func<string, (string, string)> filePathFunc)
|
||||||
{
|
{
|
||||||
var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value;
|
var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value;
|
||||||
@@ -91,7 +109,9 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
|
|
||||||
|
|
||||||
/// <summary> 流式上传 通用封装 不返回任何数据,后续还有事情处理 </summary>
|
/// <summary> 流式上传 通用封装 不返回任何数据,后续还有事情处理 </summary>
|
||||||
[Route("base")]
|
[Route("FileUpload")]
|
||||||
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
|
|
||||||
public virtual async Task FileUploadAsync(Func<string, Task<string>> filePathFunc)
|
public virtual async Task FileUploadAsync(Func<string, Task<string>> filePathFunc)
|
||||||
{
|
{
|
||||||
var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value;
|
var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value;
|
||||||
@@ -134,7 +154,9 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Route("base")]
|
[Route("FileUploadToOSS")]
|
||||||
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
|
|
||||||
public virtual async Task FileUploadToOSSAsync(Func<string, Stream, Task<string>> toMemoryStreamFunc)
|
public virtual async Task FileUploadToOSSAsync(Func<string, Stream, Task<string>> toMemoryStreamFunc)
|
||||||
{
|
{
|
||||||
var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value;
|
var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value;
|
||||||
@@ -160,10 +182,10 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary> 流式上传 Dicom上传 </summary>
|
/// <summary> 流式上传 Dicom上传 </summary>
|
||||||
[Route("base")]
|
[Route("DicomFileUpload")]
|
||||||
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
|
|
||||||
public virtual async Task DicomFileUploadAsync(Func<string, Stream, int, Task> filePathFunc, string boundary)
|
public virtual async Task DicomFileUploadAsync(Func<string, Stream, int, Task> filePathFunc, string boundary)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -225,9 +247,42 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Dicom 影像上传 临床数据 非diocm
|
#region Dicom 影像上传 临床数据 非diocm
|
||||||
|
public class UploadNoneDicomFileCommand
|
||||||
|
{
|
||||||
|
[NotDefault]
|
||||||
|
public Guid SubjectVisitId { get; set; }
|
||||||
|
|
||||||
[ApiExplorerSettings(GroupName = "Image")]
|
|
||||||
[ApiController]
|
[NotDefault]
|
||||||
|
public Guid StudyMonitorId { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public Guid? NoneDicomStudyId { get; set; }
|
||||||
|
|
||||||
|
//IR 上传的时候跟任务绑定
|
||||||
|
public Guid? VisitTaskId { get; set; }
|
||||||
|
|
||||||
|
public string RecordPath { get; set; }
|
||||||
|
|
||||||
|
public int FailedFileCount { get; set; }
|
||||||
|
|
||||||
|
public long FileSize { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public List<OSSFileDTO> UploadedFileList { get; set; } = new List<OSSFileDTO>();
|
||||||
|
|
||||||
|
|
||||||
|
public class OSSFileDTO
|
||||||
|
{
|
||||||
|
public string FilePath { get; set; }
|
||||||
|
public string FileName { get; set; }
|
||||||
|
public int FileFize { get; set; }
|
||||||
|
|
||||||
|
public string FileType { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[ApiController, ApiExplorerSettings(GroupName = "Image")]
|
||||||
public class StudyController(
|
public class StudyController(
|
||||||
IMediator _mediator,
|
IMediator _mediator,
|
||||||
QCCommon _qCCommon,
|
QCCommon _qCCommon,
|
||||||
@@ -238,14 +293,11 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>Dicom 归档</summary>
|
/// <summary>Dicom 归档</summary>
|
||||||
[HttpPost, Route("Study/ArchiveStudy")]
|
[HttpPost, Route("Study/ArchiveStudy")]
|
||||||
[DisableFormValueModelBinding]
|
[DisableFormValueModelBinding]
|
||||||
[DisableRequestSizeLimit]
|
[DisableRequestSizeLimit]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
||||||
public async Task<IResponseOutput> ArchiveStudyNew(Guid trialId, Guid subjectVisitId, string studyInstanceUid, Guid? abandonStudyId, Guid studyMonitorId,
|
public async Task<IResponseOutput> ArchiveStudyNew(Guid trialId, Guid subjectVisitId, string studyInstanceUid, Guid? abandonStudyId, Guid studyMonitorId,
|
||||||
[FromServices] ILogger<UploadDownLoadController> _logger,
|
[FromServices] ILogger<UploadDownLoadController> _logger,
|
||||||
[FromServices] IStudyService _studyService,
|
[FromServices] IStudyService _studyService,
|
||||||
@@ -392,41 +444,6 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public class UploadNoneDicomFileCommand
|
|
||||||
{
|
|
||||||
[NotDefault]
|
|
||||||
public Guid SubjectVisitId { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
[NotDefault]
|
|
||||||
public Guid StudyMonitorId { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
public Guid? NoneDicomStudyId { get; set; }
|
|
||||||
|
|
||||||
//IR 上传的时候跟任务绑定
|
|
||||||
public Guid? VisitTaskId { get; set; }
|
|
||||||
|
|
||||||
public string RecordPath { get; set; }
|
|
||||||
|
|
||||||
public int FailedFileCount { get; set; }
|
|
||||||
|
|
||||||
public long FileSize { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
public List<OSSFileDTO> UploadedFileList { get; set; } = new List<OSSFileDTO>();
|
|
||||||
|
|
||||||
|
|
||||||
public class OSSFileDTO
|
|
||||||
{
|
|
||||||
public string FilePath { get; set; }
|
|
||||||
public string FileName { get; set; }
|
|
||||||
public int FileFize { get; set; }
|
|
||||||
|
|
||||||
public string FileType { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 非dicom 上传预上传接口
|
/// 非dicom 上传预上传接口
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -435,7 +452,7 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
/// <param name="_studyMonitorRepository"></param>
|
/// <param name="_studyMonitorRepository"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Study/PreArchiveStudy")]
|
[HttpPost, Route("Study/PreArchiveStudy")]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
||||||
public async Task<IResponseOutput> PreArchiveStudy(PreArchiveStudyCommand preArchiveStudyCommand,
|
public async Task<IResponseOutput> PreArchiveStudy(PreArchiveStudyCommand preArchiveStudyCommand,
|
||||||
[FromServices] IStudyService _studyService,
|
[FromServices] IStudyService _studyService,
|
||||||
[FromServices] IRepository<StudyMonitor> _studyMonitorRepository)
|
[FromServices] IRepository<StudyMonitor> _studyMonitorRepository)
|
||||||
@@ -471,8 +488,7 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
/// <param name="_noneDicomStudyFileRepository"></param>
|
/// <param name="_noneDicomStudyFileRepository"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost("NoneDicomStudy/UploadNoneDicomFile")]
|
[HttpPost("NoneDicomStudy/UploadNoneDicomFile")]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
||||||
|
|
||||||
public async Task<IResponseOutput> UploadNoneDicomFile(UploadNoneDicomFileCommand incommand,
|
public async Task<IResponseOutput> UploadNoneDicomFile(UploadNoneDicomFileCommand incommand,
|
||||||
[FromServices] IRepository<NoneDicomStudy> _noneDicomStudyRepository,
|
[FromServices] IRepository<NoneDicomStudy> _noneDicomStudyRepository,
|
||||||
[FromServices] IRepository<StudyMonitor> _studyMonitorRepository,
|
[FromServices] IRepository<StudyMonitor> _studyMonitorRepository,
|
||||||
@@ -544,7 +560,7 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
/// <exception cref="BusinessValidationFailedException"></exception>
|
/// <exception cref="BusinessValidationFailedException"></exception>
|
||||||
[HttpPost("QCOperation/UploadVisitCheckExcel/{trialId:guid}")]
|
[HttpPost("QCOperation/UploadVisitCheckExcel/{trialId:guid}")]
|
||||||
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
||||||
|
|
||||||
public async Task<IResponseOutput> UploadVisitCheckExcel(Guid trialId, [FromServices] IOSSService oSSService, [FromServices] IRepository<InspectionFile> _inspectionFileRepository)
|
public async Task<IResponseOutput> UploadVisitCheckExcel(Guid trialId, [FromServices] IOSSService oSSService, [FromServices] IRepository<InspectionFile> _inspectionFileRepository)
|
||||||
{
|
{
|
||||||
@@ -757,146 +773,13 @@ namespace IRaCIS.Core.API.Controllers
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region 医生文件上传下载
|
|
||||||
|
|
||||||
|
|
||||||
#region DTO
|
|
||||||
public class DoctorDownloadInfo
|
|
||||||
{
|
|
||||||
public Guid Id { get; set; }
|
|
||||||
public string Name { get; set; }
|
|
||||||
public string ReviewerCode { get; set; }
|
|
||||||
|
|
||||||
public List<DownloadFileInfo> FileList { get; set; }
|
|
||||||
}
|
|
||||||
public class DownloadFileInfo
|
|
||||||
{
|
|
||||||
public string FileName { get; set; }
|
|
||||||
|
|
||||||
public string Path { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class GetDoctorPathCommand
|
|
||||||
{
|
|
||||||
public int Language { get; set; }
|
|
||||||
|
|
||||||
public List<Guid> DoctorIdList { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class GetDoctoreAttachPathCommand
|
|
||||||
{
|
|
||||||
public Guid DoctorId { get; set; }
|
|
||||||
public List<Guid> AttachmentIdList { get; set; }
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>医生文件上传下载</summary>
|
|
||||||
[ApiExplorerSettings(GroupName = "Common")]
|
|
||||||
[ApiController]
|
|
||||||
public class FileController : UploadBaseController
|
|
||||||
{
|
|
||||||
public IMapper _mapper { get; set; }
|
|
||||||
public IUserInfo _userInfo { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
private readonly IWebHostEnvironment _hostEnvironment;
|
|
||||||
|
|
||||||
private readonly IFileService _fileService;
|
|
||||||
|
|
||||||
|
|
||||||
public FileController(IMapper mapper, IUserInfo userInfo, IWebHostEnvironment hostEnvironment, IFileService fileService)
|
|
||||||
{
|
|
||||||
_fileService = fileService;
|
|
||||||
_hostEnvironment = hostEnvironment;
|
|
||||||
_mapper = mapper;
|
|
||||||
_userInfo = userInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// New 医生首页 多选 获取多个医生信息+文件路径列表 医生压缩包名称 doctorCode + "_" + doctorName _(时间戳或者随机的Guid)
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
[HttpPost, Route("file/GetOfficialResume")]
|
|
||||||
public async Task<IResponseOutput<List<DoctorDownloadInfo>>> GetOfficialResume(GetDoctorPathCommand command,
|
|
||||||
[FromServices] IRepository<Attachment> _attachmentrepository)
|
|
||||||
{
|
|
||||||
|
|
||||||
var list = await _attachmentrepository.Where(t => command.DoctorIdList.Contains(t.DoctorId) && command.Language == t.Language).GroupBy(t => new { Name = t.Doctor.FirstName + "_" + t.Doctor.LastName, ReviewerCode = t.Doctor.ReviewerCode, t.DoctorId })
|
|
||||||
.Select(g => new DoctorDownloadInfo()
|
|
||||||
{
|
|
||||||
Id = g.Key.DoctorId,
|
|
||||||
Name = g.Key.Name,
|
|
||||||
ReviewerCode = g.Key.ReviewerCode,
|
|
||||||
FileList = g.Select(t => new DownloadFileInfo() { FileName = t.FileName, Path = t.Path }).ToList()
|
|
||||||
}).ToListAsync();
|
|
||||||
|
|
||||||
return ResponseOutput.Ok(list);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// New 项目入组 勾选获取简历路径
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="command"></param>
|
|
||||||
/// <param name="_doctorService"></param>
|
|
||||||
/// <param name="_attachmentrepository"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[HttpPost, Route("file/GetTrialDoctorOfficialResume")]
|
|
||||||
public async Task<IResponseOutput<List<DoctorDownloadInfo>>> GetTrialDoctorOfficialResume(GetDoctorPathCommand command,
|
|
||||||
[FromServices] IDoctorService _doctorService,
|
|
||||||
[FromServices] IRepository<Attachment> _attachmentrepository)
|
|
||||||
{
|
|
||||||
|
|
||||||
var list = await _attachmentrepository.Where(t => command.DoctorIdList.Contains(t.DoctorId) && command.Language == t.Language && t.IsOfficial && t.Type.Equals("Resume")).GroupBy(t => new { Name = t.Doctor.FirstName + "_" + t.Doctor.LastName, ReviewerCode = t.Doctor.ReviewerCode, t.DoctorId })
|
|
||||||
.Select(g => new DoctorDownloadInfo()
|
|
||||||
{
|
|
||||||
Id = g.Key.DoctorId,
|
|
||||||
Name = g.Key.Name,
|
|
||||||
ReviewerCode = g.Key.ReviewerCode,
|
|
||||||
FileList = g.Select(t => new DownloadFileInfo() { FileName = t.FileName, Path = t.Path }).ToList()
|
|
||||||
}).ToListAsync();
|
|
||||||
|
|
||||||
return ResponseOutput.Ok(list);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// new 医生详情 勾选或者下载文件路径
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="command"></param>
|
|
||||||
/// <param name="_doctorService"></param>
|
|
||||||
/// <param name="_attachmentrepository"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[HttpPost, Route("file/GetDoctorAttachment")]
|
|
||||||
public async Task<IResponseOutput<DoctorDownloadInfo>> GetDoctorAttachment(GetDoctoreAttachPathCommand command,
|
|
||||||
[FromServices] IDoctorService _doctorService,
|
|
||||||
[FromServices] IRepository<Attachment> _attachmentrepository)
|
|
||||||
{
|
|
||||||
|
|
||||||
var find = await _attachmentrepository.Where(t => command.DoctorId == t.DoctorId && command.AttachmentIdList.Contains(t.Id)).GroupBy(t => new { Name = t.Doctor.FirstName + "_" + t.Doctor.LastName, ReviewerCode = t.Doctor.ReviewerCode, t.DoctorId })
|
|
||||||
.Select(g => new DoctorDownloadInfo()
|
|
||||||
{
|
|
||||||
Id = g.Key.DoctorId,
|
|
||||||
Name = g.Key.Name,
|
|
||||||
ReviewerCode = g.Key.ReviewerCode,
|
|
||||||
FileList = g.Select(t => new DownloadFileInfo() { FileName = t.FileName, Path = t.Path }).ToList()
|
|
||||||
}).FirstOrDefaultAsync();
|
|
||||||
|
|
||||||
return ResponseOutput.Ok(find);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
#region 项目 系统 基本文件 上传 下载 预览
|
#region 项目 系统 基本文件 上传 下载 预览
|
||||||
|
|
||||||
[ApiExplorerSettings(GroupName = "Common")]
|
[ApiController, ApiExplorerSettings(GroupName = "Common")]
|
||||||
[ApiController]
|
|
||||||
public class UploadDownLoadController : UploadBaseController
|
public class UploadDownLoadController : UploadBaseController
|
||||||
{
|
{
|
||||||
public IMapper _mapper { get; set; }
|
public IMapper _mapper { get; set; }
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using System.Threading;
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using MassTransit;
|
||||||
|
using IRaCIS.Core.Domain.Models;
|
||||||
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Hangfire;
|
||||||
|
using IRaCIS.Core.Application.Helper;
|
||||||
|
using IRaCIS.Core.Application.MassTransit.Consumer;
|
||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using MassTransit.Scheduling;
|
||||||
|
using Hangfire.Storage;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using MassTransit.Mediator;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.API.HostService;
|
||||||
|
|
||||||
|
public class HangfireHostService(IRecurringMessageScheduler _recurringMessageScheduler,
|
||||||
|
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigRepository,
|
||||||
|
IMediator _mediator,
|
||||||
|
ILogger<HangfireHostService> _logger) : IHostedService
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
public async Task StartAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("项目启动 hangfire 任务初始化 执行开始~");
|
||||||
|
|
||||||
|
|
||||||
|
//创建邮件定时任务
|
||||||
|
//项目定时任务都在default 队列
|
||||||
|
var dbJobIdList = JobStorage.Current.GetConnection().GetRecurringJobs().Where(t => t.Queue == "default").Select(t => t.Id).ToList();
|
||||||
|
|
||||||
|
foreach (var jobId in dbJobIdList)
|
||||||
|
{
|
||||||
|
HangfireJobHelper.RemoveCronJob(jobId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var taskInfoList = await _trialEmailNoticeConfigRepository.Where(t => t.Trial.TrialStatusStr == StaticData.TrialState.TrialOngoing && t.EmailCron != string.Empty && t.IsAutoSend)
|
||||||
|
.Select(t => new { t.Id, t.Code, TrialCode = t.Trial.TrialCode, t.EmailCron, t.BusinessScenarioEnum, t.TrialId })
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
foreach (var task in taskInfoList)
|
||||||
|
{
|
||||||
|
//利用主键作为任务Id
|
||||||
|
var jobId = $"{task.TrialId}({task.TrialCode})_({task.BusinessScenarioEnum})";
|
||||||
|
|
||||||
|
var trialId = task.TrialId;
|
||||||
|
|
||||||
|
HangfireJobHelper.AddOrUpdateTrialCronJob(jobId, trialId, task.BusinessScenarioEnum, task.EmailCron);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//await _recurringMessageScheduler.ScheduleRecurringPublish(new QCImageQuestionSchedule() { CronExpression = "0/3 * * * * ? " }, new MasstransiTestCommand { value = "message at " + DateTime.Now.ToString() });
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_logger.LogInformation("项目启动 hangfire 任务初始化 执行结束");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
using IRaCIS.Core.Application.MassTransit.Consumer;
|
||||||
|
using MassTransit;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.API.HostService
|
||||||
|
{
|
||||||
|
public class RecurringJobConfigurationService :
|
||||||
|
BackgroundService
|
||||||
|
{
|
||||||
|
readonly IServiceScopeFactory _scopeFactory;
|
||||||
|
|
||||||
|
public RecurringJobConfigurationService(IServiceScopeFactory scopeFactory)
|
||||||
|
{
|
||||||
|
_scopeFactory = scopeFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
|
{
|
||||||
|
await using var scope = _scopeFactory.CreateAsyncScope();
|
||||||
|
|
||||||
|
var endpoint = scope.ServiceProvider.GetRequiredService<IPublishEndpoint>();
|
||||||
|
|
||||||
|
await endpoint.AddOrUpdateRecurringJob(nameof(MasstransitTestConsumer), new MasstransiTestCommand(), x => x.Every(minutes: 1),
|
||||||
|
stoppingToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -38,11 +38,18 @@
|
|||||||
<None Remove="GrpcToken.proto" />
|
<None Remove="GrpcToken.proto" />
|
||||||
<None Remove="IRaCIS.Core.API.xml" />
|
<None Remove="IRaCIS.Core.API.xml" />
|
||||||
<None Remove="Protos\GrpcToken.proto" />
|
<None Remove="Protos\GrpcToken.proto" />
|
||||||
|
<None Remove="Resources\ip2region.xdb" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ApplicationDefinition Include="GrpcToken.proto" />
|
<ApplicationDefinition Include="GrpcToken.proto" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="Resources\ip2region.xdb">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="wwwroot\swagger\ui\abp.js">
|
<EmbeddedResource Include="wwwroot\swagger\ui\abp.js">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
@@ -62,25 +69,24 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
|
<PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
|
||||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="10.0.0" />
|
|
||||||
|
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.10">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="ConfigMapFileProvider" Version="2.0.1" />
|
<PackageReference Include="ConfigMapFileProvider" Version="2.0.1" />
|
||||||
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.14" />
|
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.14" />
|
||||||
<PackageReference Include="Hangfire.Dashboard.BasicAuthorization" Version="1.0.2" />
|
<PackageReference Include="Hangfire.Dashboard.BasicAuthorization" Version="1.0.2" />
|
||||||
<PackageReference Include="Hangfire.InMemory" Version="0.10.3" />
|
<PackageReference Include="Hangfire.InMemory" Version="1.0.0" />
|
||||||
<PackageReference Include="Hangfire.SqlServer" Version="1.8.14" />
|
<PackageReference Include="Hangfire.SqlServer" Version="1.8.14" />
|
||||||
<PackageReference Include="Invio.Extensions.Authentication.JwtBearer" Version="2.0.1" />
|
<PackageReference Include="Invio.Extensions.Authentication.JwtBearer" Version="2.0.1" />
|
||||||
<PackageReference Include="LogDashboard" Version="1.4.8" />
|
<PackageReference Include="LogDashboard" Version="1.4.8" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.8" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.10" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.0" />
|
<PackageReference Include="Serilog.AspNetCore" Version="8.0.3" />
|
||||||
<PackageReference Include="Serilog.AspNetCore" Version="8.0.2" />
|
<PackageReference Include="Serilog.Formatting.Compact" Version="3.0.0" />
|
||||||
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.1.1" />
|
<PackageReference Include="Serilog.Sinks.Email" Version="4.0.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.7.3" />
|
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.9.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -100,6 +106,12 @@
|
|||||||
<Content Update="appsettings.json">
|
<Content Update="appsettings.json">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
<Content Update="Resources\en-US.json">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="Resources\zh-CN.json">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ProjectExtensions>
|
<ProjectExtensions>
|
||||||
|
|||||||
@@ -16,7 +16,12 @@
|
|||||||
医生基本信息 、工作信息 专业信息、审核状态
|
医生基本信息 、工作信息 专业信息、审核状态
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</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)">
|
<member name="M:IRaCIS.Api.Controllers.ExtraController.#ctor(IRaCIS.Application.Interfaces.IAttachmentService,IRaCIS.Application.Interfaces.IDoctorService,IRaCIS.Application.Interfaces.IEducationService,IRaCIS.Application.Interfaces.ITrialExperienceService,IRaCIS.Application.Interfaces.IResearchPublicationService,IRaCIS.Application.Interfaces.IVacationService)">
|
||||||
|
<summary>
|
||||||
|
医生基本信息 、工作信息 专业信息、审核状态
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:IRaCIS.Api.Controllers.ExtraController.GetDoctorDetail(IRaCIS.Application.Contracts.GetDoctorDetailInDto)">
|
||||||
<summary>
|
<summary>
|
||||||
获取医生详情
|
获取医生详情
|
||||||
</summary>
|
</summary>
|
||||||
@@ -32,6 +37,22 @@
|
|||||||
<member name="M:IRaCIS.Api.Controllers.ExtraController.Login(IRaCIS.Application.Contracts.UserLoginDTO,ZiggyCreatures.Caching.Fusion.IFusionCache,IRaCIS.Core.Application.Service.IUserService,IRaCIS.Core.Application.Auth.ITokenService,IRaCIS.Core.Application.Contracts.IReadingImageTaskService,Microsoft.Extensions.Options.IOptionsMonitor{IRaCIS.Core.Domain.Share.ServiceVerifyConfigOption},Microsoft.Extensions.Options.IOptionsMonitor{IRaCIS.Core.Domain.Share.SystemEmailSendConfig},IRaCIS.Core.Application.Service.IMailVerificationService)">
|
<member name="M:IRaCIS.Api.Controllers.ExtraController.Login(IRaCIS.Application.Contracts.UserLoginDTO,ZiggyCreatures.Caching.Fusion.IFusionCache,IRaCIS.Core.Application.Service.IUserService,IRaCIS.Core.Application.Auth.ITokenService,IRaCIS.Core.Application.Contracts.IReadingImageTaskService,Microsoft.Extensions.Options.IOptionsMonitor{IRaCIS.Core.Domain.Share.ServiceVerifyConfigOption},Microsoft.Extensions.Options.IOptionsMonitor{IRaCIS.Core.Domain.Share.SystemEmailSendConfig},IRaCIS.Core.Application.Service.IMailVerificationService)">
|
||||||
<summary> 系统用户登录接口[New] </summary>
|
<summary> 系统用户登录接口[New] </summary>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="M:IRaCIS.Api.Controllers.ExtraController.OAuthCallBack(System.String,System.String)">
|
||||||
|
<summary>
|
||||||
|
回调到前端,前端调用后端的接口
|
||||||
|
参考链接:https://www.ruanyifeng.com/blog/2019/04/oauth-grant-types.html
|
||||||
|
后端通过这个code ,带上客户端信息,和授权类型 可以向单点登录提供商,获取厂商token
|
||||||
|
|
||||||
|
但是单点登录提供商提供的token 和我们系统的token 是有区别的,我们的token里面有我们业务系统的UserId,涉及到很多业务操作,所以在此出现了两种方案
|
||||||
|
1、前端使用厂商的Token。 后端通过code 获取厂商的Token 返回前端的同时返回我们系统的UserId,前段在http 请求头加上一个自定义参数,带上UserId 后端取用户Id的地方变动下,
|
||||||
|
但是除了UserId外,后端还有其他信息也是从Token取的,所以在请求头也需要带上,此外后端认证Token的方式也需要变化,改造成本稍大(如果是微服务,做这种处理还是可以的)。
|
||||||
|
2、前端还是使用我们后台自己的Token。后端通过code 获取厂商Token的同时,后端做一个隐藏登录,返回厂商的Token的同时,也返回我们系统的Token。
|
||||||
|
(像我们单体,这种方式最简单,我们用单点登录,无非就是不想记多个系统的密码,自动登录而已,其他不支持的项目改造成本也是最低的)
|
||||||
|
</summary>
|
||||||
|
<param name="type">回调的厂商类型 比如github, google, 我们用的logto ,不同的厂商回调到前端的地址可以不同的,但是请求后端的接口可以是同一个 </param>
|
||||||
|
<param name="code">在第三方平台登录成功后,回调前端的时候会返回一个code </param>
|
||||||
|
<returns></returns>
|
||||||
|
</member>
|
||||||
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.AddOrUpdateTrialInspection(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Application.Contracts.TrialCommand})">
|
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.AddOrUpdateTrialInspection(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Application.Contracts.TrialCommand})">
|
||||||
<summary> 添加实验项目-返回新增Id[AUTH]</summary>
|
<summary> 添加实验项目-返回新增Id[AUTH]</summary>
|
||||||
<returns>新记录Id</returns>
|
<returns>新记录Id</returns>
|
||||||
@@ -290,7 +311,7 @@
|
|||||||
<param name="_studyMonitorRepository"></param>
|
<param name="_studyMonitorRepository"></param>
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:IRaCIS.Core.API.Controllers.StudyController.UploadNoneDicomFile(IRaCIS.Core.API.Controllers.StudyController.UploadNoneDicomFileCommand,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.NoneDicomStudy},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.StudyMonitor},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.NoneDicomStudyFile})">
|
<member name="M:IRaCIS.Core.API.Controllers.StudyController.UploadNoneDicomFile(IRaCIS.Core.API.Controllers.UploadNoneDicomFileCommand,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.NoneDicomStudy},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.StudyMonitor},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.NoneDicomStudyFile})">
|
||||||
<summary>
|
<summary>
|
||||||
上传非Dicom 文件 支持压缩包 多文件上传
|
上传非Dicom 文件 支持压缩包 多文件上传
|
||||||
</summary>
|
</summary>
|
||||||
@@ -310,33 +331,6 @@
|
|||||||
<returns></returns>
|
<returns></returns>
|
||||||
<exception cref="T:IRaCIS.Core.Infrastructure.BusinessValidationFailedException"></exception>
|
<exception cref="T:IRaCIS.Core.Infrastructure.BusinessValidationFailedException"></exception>
|
||||||
</member>
|
</member>
|
||||||
<member name="T:IRaCIS.Core.API.Controllers.FileController">
|
|
||||||
<summary>医生文件上传下载</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:IRaCIS.Core.API.Controllers.FileController.GetOfficialResume(IRaCIS.Core.API.Controllers.GetDoctorPathCommand,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.Attachment})">
|
|
||||||
<summary>
|
|
||||||
New 医生首页 多选 获取多个医生信息+文件路径列表 医生压缩包名称 doctorCode + "_" + doctorName _(时间戳或者随机的Guid)
|
|
||||||
</summary>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
|
||||||
<member name="M:IRaCIS.Core.API.Controllers.FileController.GetTrialDoctorOfficialResume(IRaCIS.Core.API.Controllers.GetDoctorPathCommand,IRaCIS.Application.Interfaces.IDoctorService,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.Attachment})">
|
|
||||||
<summary>
|
|
||||||
New 项目入组 勾选获取简历路径
|
|
||||||
</summary>
|
|
||||||
<param name="command"></param>
|
|
||||||
<param name="_doctorService"></param>
|
|
||||||
<param name="_attachmentrepository"></param>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
|
||||||
<member name="M:IRaCIS.Core.API.Controllers.FileController.GetDoctorAttachment(IRaCIS.Core.API.Controllers.GetDoctoreAttachPathCommand,IRaCIS.Application.Interfaces.IDoctorService,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.Attachment})">
|
|
||||||
<summary>
|
|
||||||
new 医生详情 勾选或者下载文件路径
|
|
||||||
</summary>
|
|
||||||
<param name="command"></param>
|
|
||||||
<param name="_doctorService"></param>
|
|
||||||
<param name="_attachmentrepository"></param>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
|
||||||
<member name="M:IRaCIS.Core.API.Controllers.UploadDownLoadController.DownloadCommonFile(System.String,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.CommonDocument})">
|
<member name="M:IRaCIS.Core.API.Controllers.UploadDownLoadController.DownloadCommonFile(System.String,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.CommonDocument})">
|
||||||
<summary> 通用文件下载 </summary>
|
<summary> 通用文件下载 </summary>
|
||||||
</member>
|
</member>
|
||||||
@@ -352,6 +346,147 @@
|
|||||||
</summary>
|
</summary>
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="T:IRaCIS.Core.API.OAuth.LogtoParameters.Tokens">
|
||||||
|
<summary>
|
||||||
|
The token names used by Cookie and OpenID Connect middleware to store and retrieve tokens from
|
||||||
|
Logto OpenID Connect provider.
|
||||||
|
<br/>
|
||||||
|
See <see href="https://github.com/dotnet/aspnetcore/blob/4a9118c674a798aaf6379b4b7b2d8787bc688f96/src/Security/Authentication/OpenIdConnect/src/OpenIdConnectHandler.cs#L994-L1035">tokens that are stored by OpenID Connect middleware</see> for more details.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:IRaCIS.Core.API.OAuth.LogtoParameters.Scopes">
|
||||||
|
<summary>
|
||||||
|
The scope names used by Logto OpenID Connect provider to request for user information.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:IRaCIS.Core.API.OAuth.LogtoParameters.Scopes.Email">
|
||||||
|
<summary>
|
||||||
|
The scope name for requesting user's email.
|
||||||
|
Logto will issue two claims to the ID token: <c>email</c> and <c>email_verified</c>.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:IRaCIS.Core.API.OAuth.LogtoParameters.Scopes.Phone">
|
||||||
|
<summary>
|
||||||
|
The scope name for requesting user's phone number.
|
||||||
|
Logto will issue two claims to the ID token: <c>phone</c> and <c>phone_verified</c>.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:IRaCIS.Core.API.OAuth.LogtoParameters.Scopes.CustomData">
|
||||||
|
<summary>
|
||||||
|
The scope name for requesting user's custom data.
|
||||||
|
Logto will issue a claim to the response of the <c>userinfo</c> endpoint: <c>custom_data</c>.
|
||||||
|
<br/>
|
||||||
|
Note that when requesting this scope, you must set <see cref="!:LogtoOptions.GetClaimsFromUserInfoEndpoint"/> to <c>true</c>.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:IRaCIS.Core.API.OAuth.LogtoParameters.Scopes.Identities">
|
||||||
|
<summary>
|
||||||
|
The scope name for requesting user's identities.
|
||||||
|
Logto will issue a claim to the response of the <c>userinfo</c> endpoint: <c>identities</c>.
|
||||||
|
<br/>
|
||||||
|
Note that when requesting this scope, you must set <see cref="!:LogtoOptions.GetClaimsFromUserInfoEndpoint"/> to <c>true</c>.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:IRaCIS.Core.API.OAuth.LogtoParameters.Claims">
|
||||||
|
<summary>
|
||||||
|
The claim names used by Logto OpenID Connect provider for ID token and userinfo endpoint.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:IRaCIS.Core.API.OAuth.LogtoParameters.Claims.Issuer">
|
||||||
|
<summary>
|
||||||
|
The claim name for the issuer identifier for whom issued the token.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:IRaCIS.Core.API.OAuth.LogtoParameters.Claims.Subject">
|
||||||
|
<summary>
|
||||||
|
The claim name for the subject identifier for whom the token is intended (user ID).
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:IRaCIS.Core.API.OAuth.LogtoParameters.Claims.Audience">
|
||||||
|
<summary>
|
||||||
|
The claim name for the audience that the token is intended for, which is the client ID.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:IRaCIS.Core.API.OAuth.LogtoParameters.Claims.Expiration">
|
||||||
|
<summary>
|
||||||
|
The claim name for the expiration time of the token (in seconds).
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:IRaCIS.Core.API.OAuth.LogtoParameters.Claims.IssuedAt">
|
||||||
|
<summary>
|
||||||
|
The claim name for the time at which the token was issued (in seconds).
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:IRaCIS.Core.API.OAuth.LogtoParameters.Claims.Name">
|
||||||
|
<summary>
|
||||||
|
The claim name for the user's full name.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:IRaCIS.Core.API.OAuth.LogtoParameters.Claims.Username">
|
||||||
|
<summary>
|
||||||
|
The claim name for user's username.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:IRaCIS.Core.API.OAuth.LogtoParameters.Claims.Picture">
|
||||||
|
<summary>
|
||||||
|
The claim name for user's profile picture URL.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:IRaCIS.Core.API.OAuth.LogtoParameters.Claims.Email">
|
||||||
|
<summary>
|
||||||
|
The claim name for user's email.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:IRaCIS.Core.API.OAuth.LogtoParameters.Claims.EmailVerified">
|
||||||
|
<summary>
|
||||||
|
The claim name for user's email verification status.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:IRaCIS.Core.API.OAuth.LogtoParameters.Claims.PhoneNumber">
|
||||||
|
<summary>
|
||||||
|
The claim name for user's phone number.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:IRaCIS.Core.API.OAuth.LogtoParameters.Claims.PhoneNumberVerified">
|
||||||
|
<summary>
|
||||||
|
The claim name for user's phone number verification status.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:IRaCIS.Core.API.OAuth.LogtoParameters.Claims.CustomData">
|
||||||
|
<summary>
|
||||||
|
The claim name for user's custom data.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:IRaCIS.Core.API.OAuth.LogtoParameters.Claims.Identities">
|
||||||
|
<summary>
|
||||||
|
The claim name for user's identities.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:IRaCIS.Core.API.OAuth.LogtoTokenResponse.AccessToken">
|
||||||
|
<summary>
|
||||||
|
The access token issued by the Logto authorization server.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:IRaCIS.Core.API.OAuth.LogtoTokenResponse.TokenType">
|
||||||
|
<summary>
|
||||||
|
The type of the token issued by the Logto authorization server.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:IRaCIS.Core.API.OAuth.LogtoTokenResponse.ExpiresIn">
|
||||||
|
<summary>
|
||||||
|
The lifetime in seconds of the access token.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:IRaCIS.Core.API.OAuth.LogtoTokenResponse.RefreshToken">
|
||||||
|
<summary>
|
||||||
|
The refresh token, which can be used to obtain new access tokens using the same authorization grant.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:IRaCIS.Core.API.OAuth.LogtoTokenResponse.IdToken">
|
||||||
|
<summary>
|
||||||
|
The ID token, which can be used to verify the identity of the user.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
<member name="T:IRaCIS.Core.API.IpPolicyRateLimitSetup">
|
<member name="T:IRaCIS.Core.API.IpPolicyRateLimitSetup">
|
||||||
<summary>
|
<summary>
|
||||||
IPLimit限流 启动服务
|
IPLimit限流 启动服务
|
||||||
@@ -367,13 +502,10 @@
|
|||||||
序列化,反序列化的时候,处理时间 时区转换
|
序列化,反序列化的时候,处理时间 时区转换
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:IRaCIS.Core.API.NullToEmptyStringResolver.CreateProperties(System.Type,Newtonsoft.Json.MemberSerialization)">
|
<member name="T:IRaCIS.Core.API.NullToEmptyStringResolver">
|
||||||
<summary>
|
<summary>
|
||||||
创建属性
|
LowerCamelCaseJsonAttribute 可以设置类小写返回给前端
|
||||||
</summary>
|
</summary>
|
||||||
<param name="type">类型</param>
|
|
||||||
<param name="memberSerialization">序列化成员</param>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
</member>
|
||||||
<member name="T:ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt.CustomHSJWTService">
|
<member name="T:ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt.CustomHSJWTService">
|
||||||
<summary>
|
<summary>
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.API.OAuth;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static class LogtoParameters
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The token names used by Cookie and OpenID Connect middleware to store and retrieve tokens from
|
||||||
|
/// Logto OpenID Connect provider.
|
||||||
|
/// <br/>
|
||||||
|
/// See <see href="https://github.com/dotnet/aspnetcore/blob/4a9118c674a798aaf6379b4b7b2d8787bc688f96/src/Security/Authentication/OpenIdConnect/src/OpenIdConnectHandler.cs#L994-L1035">tokens that are stored by OpenID Connect middleware</see> for more details.
|
||||||
|
/// </summary>
|
||||||
|
public static class Tokens
|
||||||
|
{
|
||||||
|
public const string AccessToken = OpenIdConnectParameterNames.AccessToken;
|
||||||
|
public const string ExpiresAt = "expires_at";
|
||||||
|
public const string AccessTokenForResource = $"{AccessToken}.resource";
|
||||||
|
public const string ExpiresAtForResource = $"{ExpiresAt}.resource";
|
||||||
|
public const string RefreshToken = OpenIdConnectParameterNames.RefreshToken;
|
||||||
|
public const string IdToken = OpenIdConnectParameterNames.IdToken;
|
||||||
|
public const string TokenType = OpenIdConnectParameterNames.TokenType;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The scope names used by Logto OpenID Connect provider to request for user information.
|
||||||
|
/// </summary>
|
||||||
|
public static class Scopes
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The scope name for requesting user's email.
|
||||||
|
/// Logto will issue two claims to the ID token: <c>email</c> and <c>email_verified</c>.
|
||||||
|
/// </summary>
|
||||||
|
public const string Email = "email";
|
||||||
|
/// <summary>
|
||||||
|
/// The scope name for requesting user's phone number.
|
||||||
|
/// Logto will issue two claims to the ID token: <c>phone</c> and <c>phone_verified</c>.
|
||||||
|
/// </summary>
|
||||||
|
public const string Phone = "phone";
|
||||||
|
/// <summary>
|
||||||
|
/// The scope name for requesting user's custom data.
|
||||||
|
/// Logto will issue a claim to the response of the <c>userinfo</c> endpoint: <c>custom_data</c>.
|
||||||
|
/// <br/>
|
||||||
|
/// Note that when requesting this scope, you must set <see cref="LogtoOptions.GetClaimsFromUserInfoEndpoint"/> to <c>true</c>.
|
||||||
|
/// </summary>
|
||||||
|
public const string CustomData = "custom_data";
|
||||||
|
/// <summary>
|
||||||
|
/// The scope name for requesting user's identities.
|
||||||
|
/// Logto will issue a claim to the response of the <c>userinfo</c> endpoint: <c>identities</c>.
|
||||||
|
/// <br/>
|
||||||
|
/// Note that when requesting this scope, you must set <see cref="LogtoOptions.GetClaimsFromUserInfoEndpoint"/> to <c>true</c>.
|
||||||
|
/// </summary>
|
||||||
|
public const string Identities = "identities";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The claim names used by Logto OpenID Connect provider for ID token and userinfo endpoint.
|
||||||
|
/// </summary>
|
||||||
|
public static class Claims
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The claim name for the issuer identifier for whom issued the token.
|
||||||
|
/// </summary>
|
||||||
|
public const string Issuer = "iss";
|
||||||
|
/// <summary>
|
||||||
|
/// The claim name for the subject identifier for whom the token is intended (user ID).
|
||||||
|
/// </summary>
|
||||||
|
public const string Subject = "sub";
|
||||||
|
/// <summary>
|
||||||
|
/// The claim name for the audience that the token is intended for, which is the client ID.
|
||||||
|
/// </summary>
|
||||||
|
public const string Audience = "aud";
|
||||||
|
/// <summary>
|
||||||
|
/// The claim name for the expiration time of the token (in seconds).
|
||||||
|
/// </summary>
|
||||||
|
public const string Expiration = "exp";
|
||||||
|
/// <summary>
|
||||||
|
/// The claim name for the time at which the token was issued (in seconds).
|
||||||
|
/// </summary>
|
||||||
|
public const string IssuedAt = "iat";
|
||||||
|
/// <summary>
|
||||||
|
/// The claim name for the user's full name.
|
||||||
|
/// </summary>
|
||||||
|
public const string Name = "name";
|
||||||
|
/// <summary>
|
||||||
|
/// The claim name for user's username.
|
||||||
|
/// </summary>
|
||||||
|
public const string Username = "username";
|
||||||
|
/// <summary>
|
||||||
|
/// The claim name for user's profile picture URL.
|
||||||
|
/// </summary>
|
||||||
|
public const string Picture = "picture";
|
||||||
|
/// <summary>
|
||||||
|
/// The claim name for user's email.
|
||||||
|
/// </summary>
|
||||||
|
public const string Email = "email";
|
||||||
|
/// <summary>
|
||||||
|
/// The claim name for user's email verification status.
|
||||||
|
/// </summary>
|
||||||
|
public const string EmailVerified = "email_verified";
|
||||||
|
/// <summary>
|
||||||
|
/// The claim name for user's phone number.
|
||||||
|
/// </summary>
|
||||||
|
public const string PhoneNumber = "phone_number";
|
||||||
|
/// <summary>
|
||||||
|
/// The claim name for user's phone number verification status.
|
||||||
|
/// </summary>
|
||||||
|
public const string PhoneNumberVerified = "phone_number_verified";
|
||||||
|
/// <summary>
|
||||||
|
/// The claim name for user's custom data.
|
||||||
|
/// </summary>
|
||||||
|
public const string CustomData = "custom_data";
|
||||||
|
/// <summary>
|
||||||
|
/// The claim name for user's identities.
|
||||||
|
/// </summary>
|
||||||
|
public const string Identities = "identities";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.API.OAuth;
|
||||||
|
|
||||||
|
public class LogtoTokenResponse
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The access token issued by the Logto authorization server.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("access_token")]
|
||||||
|
public string AccessToken { get; set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The type of the token issued by the Logto authorization server.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("token_type")]
|
||||||
|
public string TokenType { get; set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The lifetime in seconds of the access token.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("expires_in")]
|
||||||
|
public int ExpiresIn { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The refresh token, which can be used to obtain new access tokens using the same authorization grant.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("refresh_token")]
|
||||||
|
public string? RefreshToken { get; set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The ID token, which can be used to verify the identity of the user.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("id_token")]
|
||||||
|
public string? IdToken { get; set; } = null;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
using Org.BouncyCastle.Tls;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.API.OAuth;
|
||||||
|
|
||||||
|
public class LogtoUser
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
public string Username { get; set; }
|
||||||
|
public string PrimaryEmail { get; set; }
|
||||||
|
public string PrimaryPhone { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Avatar { get; set; }
|
||||||
|
public Dictionary<string, object> CustomData { get; set; } // Assuming customData can be any object
|
||||||
|
public Dictionary<string, object> Identities { get; set; }
|
||||||
|
|
||||||
|
public Dictionary<string, object> Profile { get; set; }
|
||||||
|
public string ApplicationId { get; set; }
|
||||||
|
public bool IsSuspended { get; set; }
|
||||||
|
public bool HasPassword { get; set; }
|
||||||
|
}
|
||||||
+89
-208
@@ -1,43 +1,34 @@
|
|||||||
using System;
|
using IRaCIS.Core.API;
|
||||||
using Autofac.Extensions.DependencyInjection;
|
using IRaCIS.Core.API.HostService;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using IRaCIS.Core.Application.BusinessFilter;
|
||||||
using Microsoft.Extensions.Hosting;
|
using IRaCIS.Core.Application.Filter;
|
||||||
using Microsoft.Extensions.Configuration;
|
using IRaCIS.Core.Application.MassTransit.Consumer;
|
||||||
using Serilog;
|
using IRaCIS.Core.Application.Service;
|
||||||
using System.Threading.Tasks;
|
using IRaCIS.Core.Application.Service.BusinessFilter;
|
||||||
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
|
using IRaCIS.Core.Infrastructure.Extention;
|
||||||
|
using LogDashboard;
|
||||||
using MassTransit;
|
using MassTransit;
|
||||||
using MassTransit.NewIdProviders;
|
using MassTransit.NewIdProviders;
|
||||||
using System.IO;
|
|
||||||
using IRaCIS.Core.Domain.Share;
|
|
||||||
using IRaCIS.Core.Infra.EFCore;
|
|
||||||
using IRaCIS.Core.Application.Helper;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using IRaCIS.Core.API;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Autofac;
|
|
||||||
using Microsoft.AspNetCore.Http.Features;
|
|
||||||
using Microsoft.AspNetCore.SignalR;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using IRaCIS.Core.Application.Filter;
|
|
||||||
using Microsoft.AspNetCore.HttpOverrides;
|
|
||||||
using IRaCIS.Core.Application.Service.BackGroundJob;
|
|
||||||
using LogDashboard;
|
|
||||||
using FellowOakDicom.Network;
|
|
||||||
using IRaCIS.Core.Application.Service.ImageAndDoc;
|
|
||||||
using IP2Region.Net.Abstractions;
|
|
||||||
using IP2Region.Net.XDB;
|
|
||||||
using IRaCIS.Core.Application.BusinessFilter;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using IRaCIS.Core.Infrastructure.Extention;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Localization;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Microsoft.AspNetCore.Diagnostics;
|
using Serilog;
|
||||||
using IRaCIS.Core.Application.MassTransit.Command;
|
using System;
|
||||||
using IRaCIS.Core.Application.MassTransit.Consumer;
|
using System.Collections.Generic;
|
||||||
using DocumentFormat.OpenXml.InkML;
|
using System.IO;
|
||||||
using IRaCIS.Core.Domain;
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
|
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
|
||||||
AppContext.SetSwitch("Npgsql.DisableDateTimeInfinityConversions", true);
|
AppContext.SetSwitch("Npgsql.DisableDateTimeInfinityConversions", true);
|
||||||
|
|
||||||
#region 获取环境变量
|
#region 获取环境变量
|
||||||
//以配置文件为准,否则 从url中取环境值(服务以命令行传递参数启动,配置文件配置了就不需要传递环境参数)
|
//以配置文件为准,否则 从url中取环境值(服务以命令行传递参数启动,配置文件配置了就不需要传递环境参数)
|
||||||
var config = new ConfigurationBuilder()
|
var config = new ConfigurationBuilder()
|
||||||
@@ -56,29 +47,14 @@ if (string.IsNullOrWhiteSpace(enviromentName))
|
|||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
// Serilog
|
||||||
|
SerilogExtension.AddSerilogSetup(enviromentName);
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
|
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
|
||||||
{
|
{
|
||||||
EnvironmentName = enviromentName
|
EnvironmentName = enviromentName
|
||||||
});
|
});
|
||||||
|
|
||||||
#region 兼容windows 服务命令行的方式
|
|
||||||
|
|
||||||
//foreach (var arg in args)
|
|
||||||
//{
|
|
||||||
// Console.WriteLine(arg);
|
|
||||||
//}
|
|
||||||
|
|
||||||
int urlsIndex = Array.FindIndex(args, arg => arg != null && arg.StartsWith("--urls"));
|
|
||||||
|
|
||||||
if (urlsIndex > -1)
|
|
||||||
{
|
|
||||||
var url = args[urlsIndex].Substring("--urls=".Length);
|
|
||||||
Console.WriteLine(url);
|
|
||||||
builder.WebHost.UseUrls(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 主机配置
|
#region 主机配置
|
||||||
|
|
||||||
|
|
||||||
@@ -87,50 +63,47 @@ NewId.SetProcessIdProvider(new CurrentProcessIdProvider());
|
|||||||
builder.Configuration.AddJsonFile(ConfigMapFileProvider.FromRelativePath(""), "appsettings.json", false, true)
|
builder.Configuration.AddJsonFile(ConfigMapFileProvider.FromRelativePath(""), "appsettings.json", false, true)
|
||||||
.AddJsonFile(ConfigMapFileProvider.FromRelativePath(""), $"appsettings.{enviromentName}.json", false, true);
|
.AddJsonFile(ConfigMapFileProvider.FromRelativePath(""), $"appsettings.{enviromentName}.json", false, true);
|
||||||
|
|
||||||
builder.Host
|
builder.Host.UseSerilog();
|
||||||
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
|
|
||||||
.ConfigureContainer<ContainerBuilder>(containerBuilder =>
|
|
||||||
{
|
|
||||||
containerBuilder.RegisterModule<AutofacModuleSetup>();
|
|
||||||
})
|
|
||||||
.UseWindowsService().UseSerilog();
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
#region 配置服务
|
#region 配置服务
|
||||||
var _configuration = builder.Configuration;
|
var _configuration = builder.Configuration;
|
||||||
|
|
||||||
|
//手动注册服务
|
||||||
|
builder.Services.ConfigureServices(_configuration);
|
||||||
|
|
||||||
|
builder.Services.AddHostedService<HangfireHostService>();
|
||||||
|
|
||||||
|
//minimal api 异常处理
|
||||||
|
builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
|
||||||
|
//builder.Services.AddProblemDetails();
|
||||||
|
|
||||||
//健康检查
|
//健康检查
|
||||||
builder.Services.AddHealthChecks();
|
builder.Services.AddHealthChecks();
|
||||||
|
builder.Services.AddSerilog();
|
||||||
//本地化
|
//本地化
|
||||||
builder.Services.AddJsonLocalization(options => options.ResourcesPath = "Resources");
|
builder.Services.AddJsonLocalization(options => options.ResourcesPath = "Resources");
|
||||||
|
|
||||||
// 异常、参数统一验证过滤器、Json序列化配置、字符串参数绑型统一Trim()
|
// 异常、参数统一验证过滤器、Json序列化配置、字符串参数绑型统一Trim()
|
||||||
builder.Services.AddControllers(options =>
|
builder.Services.AddControllers(options =>
|
||||||
{
|
{
|
||||||
//options.Filters.Add<LogActionFilter>();
|
|
||||||
options.Filters.Add<ModelActionFilter>();
|
options.Filters.Add<ModelActionFilter>();
|
||||||
options.Filters.Add<ProjectExceptionFilter>();
|
options.Filters.Add<ProjectExceptionFilter>();
|
||||||
options.Filters.Add<UnitOfWorkFilter>();
|
options.Filters.Add<UnitOfWorkFilter>();
|
||||||
//options.Filters.Add<EncreptApiResultFilter>(10);
|
|
||||||
options.Filters.Add<LimitUserRequestAuthorization>();
|
options.Filters.Add<LimitUserRequestAuthorization>();
|
||||||
|
options.Filters.Add<TrialGlobalLimitActionFilter>();
|
||||||
|
|
||||||
})
|
})
|
||||||
.AddNewtonsoftJsonSetup(builder.Services); // NewtonsoftJson 序列化 处理
|
.AddNewtonsoftJsonSetup(builder.Services); // NewtonsoftJson 序列化 处理
|
||||||
|
|
||||||
builder.Services.AddOptions().Configure<SystemEmailSendConfig>(_configuration.GetSection("SystemEmailSendConfig"));
|
// Panda动态WebApi + UnifiedApiResultFilter + 省掉控制器代码
|
||||||
builder.Services.AddOptions().Configure<ServiceVerifyConfigOption>(_configuration.GetSection("BasicSystemConfig"));
|
|
||||||
builder.Services.AddOptions().Configure<AliyunOSSOptions>(_configuration.GetSection("AliyunOSS"));
|
|
||||||
builder.Services.AddOptions().Configure<ObjectStoreServiceOptions>(_configuration.GetSection("ObjectStoreService"));
|
|
||||||
builder.Services.AddOptions().Configure<IRCEncreptOption>(_configuration.GetSection("EncrypteResponseConfig"));
|
|
||||||
builder.Services.AddOptions().Configure<SystemPacsConfig>(_configuration.GetSection("SystemPacsConfig"));
|
|
||||||
|
|
||||||
builder.Services.Configure<IRaCISBasicConfigOption>(_configuration.GetSection("IRaCISBasicConfig"));
|
|
||||||
|
|
||||||
|
|
||||||
//动态WebApi + UnifiedApiResultFilter 省掉控制器代码
|
|
||||||
builder.Services.AddDynamicWebApiSetup();
|
builder.Services.AddDynamicWebApiSetup();
|
||||||
|
//MinimalAPI
|
||||||
|
builder.Services.AddMasaMinimalAPiSetUp();
|
||||||
|
|
||||||
//AutoMapper
|
//AutoMapper
|
||||||
builder.Services.AddAutoMapperSetup();
|
builder.Services.AddAutoMapperSetup();
|
||||||
//EF ORM QueryWithNoLock
|
//EF ORM QueryWithNoLock
|
||||||
@@ -142,91 +115,33 @@ builder.Services.AddSwaggerSetup();
|
|||||||
//JWT Token 验证
|
//JWT Token 验证
|
||||||
builder.Services.AddJWTAuthSetup(_configuration);
|
builder.Services.AddJWTAuthSetup(_configuration);
|
||||||
|
|
||||||
// MediatR 进程内消息 事件解耦 从程序集中 注册命令和handler对应关系
|
//MassTransit
|
||||||
//builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblyContaining<ConsistencyVerificationHandler>());
|
builder.Services.AddMassTransitSetup();
|
||||||
|
|
||||||
#region MassTransit
|
|
||||||
//masstransit组件 也支持MediatR 中介者模式,但是支持分布式,考虑后续,所以在次替代MediatR
|
|
||||||
//参考链接:https://masstransit.io/documentation/concepts/mediator#scoped-mediator
|
|
||||||
builder.Services.AddMediator(cfg =>
|
|
||||||
{
|
|
||||||
cfg.AddConsumer<ConsistencyCheckConsumer>();
|
|
||||||
cfg.AddConsumer<AddSubjectTriggerConsumer>();
|
|
||||||
cfg.AddConsumer<AddSubjectTriggerConsumer2>();
|
|
||||||
//cfg.ConfigureMediator((context, cfg) => cfg.UseHttpContextScopeFilter(context));
|
|
||||||
});
|
|
||||||
|
|
||||||
//添加 MassTransit 和 InMemory 传输
|
|
||||||
builder.Services.AddMassTransit(cfg =>
|
|
||||||
{
|
|
||||||
// 注册消费者
|
|
||||||
cfg.AddConsumer<AddSubjectTriggerConsumer>(); // 替换为你的消费者类
|
|
||||||
cfg.AddConsumer<AddSubjectTriggerConsumer2>();
|
|
||||||
|
|
||||||
// 使用 InMemory 作为消息传递机制
|
|
||||||
cfg.UsingInMemory((context, cfg) =>
|
|
||||||
{
|
|
||||||
// 这里可以进行额外的配置
|
|
||||||
cfg.ConfigureEndpoints(context); // 自动配置所有消费者的端点
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
#region FusionCache
|
|
||||||
|
|
||||||
|
// FusionCache
|
||||||
builder.Services.AddFusionCache();
|
builder.Services.AddFusionCache();
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
// EasyCaching 缓存
|
|
||||||
//builder.Services.AddEasyCachingSetup(_configuration);
|
|
||||||
|
|
||||||
// hangfire 定时任务框架 有界面,更友好~
|
// hangfire 定时任务框架 有界面,更友好~
|
||||||
builder.Services.AddhangfireSetup(_configuration);
|
builder.Services.AddhangfireSetup(_configuration);
|
||||||
|
|
||||||
|
|
||||||
//Serilog 日志可视化 LogDashboard日志
|
//Serilog 日志可视化 LogDashboard日志
|
||||||
builder.Services.AddLogDashboardSetup();
|
builder.Services.AddLogDashboardSetup();
|
||||||
|
|
||||||
|
|
||||||
builder.Services.AddJsonConfigSetup(_configuration);
|
|
||||||
//转发头设置 获取真实IP
|
|
||||||
builder.Services.Configure<ForwardedHeadersOptions>(options =>
|
|
||||||
{
|
|
||||||
options.ForwardedHeaders =
|
|
||||||
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
|
|
||||||
});
|
|
||||||
//Dicom影像渲染图片 跨平台
|
//Dicom影像渲染图片 跨平台
|
||||||
builder.Services.AddDicomSetup();
|
builder.Services.AddDicomSetup();
|
||||||
|
|
||||||
// 实时应用
|
// 实时应用
|
||||||
builder.Services.AddSignalR();
|
builder.Services.AddSignalR();
|
||||||
|
|
||||||
builder.Services.AddSingleton<IUserIdProvider, IRaCISUserIdProvider>();
|
|
||||||
|
|
||||||
|
//// 添加反伪造服务
|
||||||
builder.Services.AddSingleton<ISearcher>(new Searcher(CachePolicy.Content, Path.Combine(AppContext.BaseDirectory, StaticData.Folder.Resources, "ip2region.xdb")));
|
//builder.Services.AddAntiforgery(options =>
|
||||||
|
|
||||||
//builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
|
|
||||||
//builder.Services.AddProblemDetails();
|
|
||||||
|
|
||||||
|
|
||||||
#region 历史废弃配置
|
|
||||||
//builder.Services.AddMemoryCache();
|
|
||||||
////上传限制 配置
|
|
||||||
//builder.Services.Configure<FormOptions>(options =>
|
|
||||||
//{
|
//{
|
||||||
// options.MultipartBodyLengthLimit = int.MaxValue;
|
// // 可选:设置自定义的头部名称以支持 AJAX 请求等
|
||||||
// options.ValueCountLimit = int.MaxValue;
|
// options.HeaderName = "X-XSRF-TOKEN";
|
||||||
// options.ValueLengthLimit = int.MaxValue;
|
|
||||||
//});
|
//});
|
||||||
//IP 限流 可设置白名单 或者黑名单
|
|
||||||
//services.AddIpPolicyRateLimitSetup(_configuration);
|
//builder.Services.AddAntiforgery();
|
||||||
// 用户类型 策略授权
|
|
||||||
//services.AddAuthorizationPolicySetup(_configuration);
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -235,9 +150,8 @@ var env = app.Environment;
|
|||||||
|
|
||||||
#region 配置中间件
|
#region 配置中间件
|
||||||
|
|
||||||
app.UseMiddleware<EncryptionRequestMiddleware>();
|
|
||||||
|
|
||||||
// Configure the HTTP request pipeline.
|
app.UseMiddleware<EncryptionRequestMiddleware>();
|
||||||
|
|
||||||
#region 异常处理 全局业务异常已统一处理了,非业务错误会来到这里 400 -500状态码
|
#region 异常处理 全局业务异常已统一处理了,非业务错误会来到这里 400 -500状态码
|
||||||
|
|
||||||
@@ -260,54 +174,8 @@ app.UseStatusCodePages(async context =>
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//app.UseExceptionHandler(o => { });
|
//app.UseExceptionHandler();
|
||||||
|
app.UseExceptionHandler(o => { });
|
||||||
//这里没生效,原因未知,官方文档也是这种写法,也用了GlobalExceptionHandler 尝试,还是不行,怀疑框架bug
|
|
||||||
//app.UseExceptionHandler(configure =>
|
|
||||||
//{
|
|
||||||
// configure.Run(async context =>
|
|
||||||
// {
|
|
||||||
// var exceptionHandlerPathFeature = context.Features.Get<IExceptionHandlerPathFeature>();
|
|
||||||
//var ex = exceptionHandlerPathFeature?.Error;
|
|
||||||
//context.Response.ContentType = "application/json";
|
|
||||||
|
|
||||||
//if (ex != null)
|
|
||||||
//{
|
|
||||||
// var errorInfo = $"Exception: {ex.Message}[{ex.StackTrace}]" + (ex.InnerException != null ? $" InnerException: {ex.InnerException.Message}[{ex.InnerException.StackTrace}]" : "");
|
|
||||||
|
|
||||||
// await context.Response.WriteAsync(JsonConvert.SerializeObject(ResponseOutput.NotOk($"{ex?.Message}")));
|
|
||||||
|
|
||||||
// Log.Logger.Error(errorInfo);
|
|
||||||
|
|
||||||
|
|
||||||
//}
|
|
||||||
|
|
||||||
// });
|
|
||||||
//});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
//本地化
|
|
||||||
app.UseLocalization();
|
|
||||||
|
|
||||||
app.UseForwardedHeaders();
|
|
||||||
|
|
||||||
//响应压缩
|
|
||||||
app.UseResponseCompression();
|
|
||||||
|
|
||||||
|
|
||||||
//不需要 token 访问的静态文件 wwwroot css, JavaScript, and images don't require authentication.
|
|
||||||
app.UseStaticFiles();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//LogDashboard
|
|
||||||
app.UseLogDashboard("/LogDashboard");
|
|
||||||
|
|
||||||
//hangfire
|
|
||||||
app.UseHangfireConfig(env);
|
|
||||||
|
|
||||||
#region 暂时废弃
|
#region 暂时废弃
|
||||||
|
|
||||||
@@ -322,46 +190,61 @@ app.UseHangfireConfig(env);
|
|||||||
//{
|
//{
|
||||||
// //app.UseHsts();
|
// //app.UseHsts();
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
//app.UseIRacisHostStaticFileStore(env);
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
app.UseIRacisHostStaticFileStore(env);
|
||||||
|
|
||||||
|
//本地化
|
||||||
|
await app.UseLocalization(app.Services);
|
||||||
|
|
||||||
|
app.UseForwardedHeaders();
|
||||||
|
|
||||||
|
//响应压缩
|
||||||
|
app.UseResponseCompression();
|
||||||
|
|
||||||
|
//不需要 token 访问的静态文件 wwwroot css, JavaScript, and images don't require authentication.
|
||||||
|
app.UseStaticFiles();
|
||||||
|
|
||||||
|
//LogDashboard
|
||||||
|
app.UseLogDashboard("/LogDashboard");
|
||||||
|
|
||||||
|
//hangfire
|
||||||
|
app.UseHangfireConfig(env);
|
||||||
|
|
||||||
|
// Swagger
|
||||||
SwaggerSetup.Configure(app, env);
|
SwaggerSetup.Configure(app, env);
|
||||||
|
|
||||||
|
//serilog 记录请求的用户信息
|
||||||
////serilog 记录请求的用户信息
|
|
||||||
app.UseSerilogConfig(env);
|
app.UseSerilogConfig(env);
|
||||||
|
|
||||||
app.UseRouting();
|
app.UseRouting();
|
||||||
|
|
||||||
app.UseCors(t => t.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
|
app.UseCors(t => t.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
|
||||||
|
|
||||||
//app.UseIRacisHostStaticFileStore(env);
|
|
||||||
|
|
||||||
|
|
||||||
app.UseAuthentication();
|
app.UseAuthentication();
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
|
||||||
|
//Map MinimalAPI routes
|
||||||
|
app.MapMasaMinimalAPIs();
|
||||||
|
|
||||||
|
//// 这里添加反伪造中间件
|
||||||
|
//app.UseAntiforgery();
|
||||||
app.MapControllers();
|
app.MapControllers();
|
||||||
|
|
||||||
app.MapHub<UploadHub>("/UploadHub");
|
app.MapHub<UploadHub>("/UploadHub");
|
||||||
app.MapHealthChecks("/health");
|
app.MapHealthChecks("/health");
|
||||||
|
|
||||||
|
|
||||||
// Serilog
|
|
||||||
SerilogExtension.AddSerilogSetup(enviromentName, app.Services);
|
|
||||||
|
|
||||||
|
|
||||||
var hangfireJobService = app.Services.GetRequiredService<IIRaCISHangfireJob>();
|
|
||||||
|
|
||||||
await hangfireJobService.InitHangfireJobTaskAsync();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
#region 运行环境 部署平台
|
#region 运行环境 部署平台
|
||||||
@@ -381,9 +264,6 @@ try
|
|||||||
Log.Logger.Warning($"当前部署平台环境:OSX or FreeBSD");
|
Log.Logger.Warning($"当前部署平台环境:OSX or FreeBSD");
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
Log.Logger.Warning($"ContentRootPath:{env.ContentRootPath}");
|
Log.Logger.Warning($"ContentRootPath:{env.ContentRootPath}");
|
||||||
|
|
||||||
|
|
||||||
@@ -394,10 +274,11 @@ try
|
|||||||
|
|
||||||
//Log.Logger.Warning($"ContentRootPath——GetParent:{Directory.GetParent(env.ContentRootPath).Parent.FullName}");
|
//Log.Logger.Warning($"ContentRootPath——GetParent:{Directory.GetParent(env.ContentRootPath).Parent.FullName}");
|
||||||
//Log.Logger.Warning($"ContentRootPath——xx:{Path.GetDirectoryName(Path.GetDirectoryName(env.ContentRootPath))}");
|
//Log.Logger.Warning($"ContentRootPath——xx:{Path.GetDirectoryName(Path.GetDirectoryName(env.ContentRootPath))}");
|
||||||
|
#endregion
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
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)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,270 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +1,9 @@
|
|||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Microsoft.IdentityModel.Tokens;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IdentityModel.Tokens.Jwt;
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
using System.Linq;
|
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt
|
namespace ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Microsoft.IdentityModel.Tokens;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IdentityModel.Tokens.Jwt;
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt
|
namespace ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,9 +1,4 @@
|
|||||||
using System;
|
namespace ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt
|
|
||||||
{
|
{
|
||||||
public interface ICustomJWTService
|
public interface ICustomJWTService
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,9 +1,4 @@
|
|||||||
using System;
|
namespace ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt
|
|
||||||
{
|
{
|
||||||
public class JWTTokenOptions
|
public class JWTTokenOptions
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt
|
namespace ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
using Hangfire;
|
using Hangfire;
|
||||||
using Hangfire.Dashboard;
|
using Hangfire.Dashboard;
|
||||||
using Hangfire.Dashboard.BasicAuthorization;
|
using Hangfire.Dashboard.BasicAuthorization;
|
||||||
using IRaCIS.Core.Application.Service.BackGroundJob;
|
|
||||||
using IRaCIS.Core.API.Filter;
|
|
||||||
using IRaCIS.Core.Application.Helper;
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
|
||||||
|
|||||||
+1
-10
@@ -1,21 +1,12 @@
|
|||||||
using Azure;
|
using IRaCIS.Core.Application.Helper;
|
||||||
using IRaCIS.Core.Application.Helper;
|
|
||||||
using IRaCIS.Core.Domain.Share;
|
using IRaCIS.Core.Domain.Share;
|
||||||
using IRaCIS.Core.Infrastructure.Extention;
|
using IRaCIS.Core.Infrastructure.Extention;
|
||||||
using Microsoft.AspNetCore.Builder;
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.AspNetCore.StaticFiles;
|
|
||||||
using Microsoft.Extensions.FileProviders;
|
using Microsoft.Extensions.FileProviders;
|
||||||
using Microsoft.Extensions.FileProviders.Physical;
|
using Microsoft.Extensions.FileProviders.Physical;
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
using Microsoft.Extensions.Hosting.Internal;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using Microsoft.VisualBasic;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using SharpCompress.Common;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|||||||
@@ -1,15 +1,24 @@
|
|||||||
using IRaCIS.Core.Domain.Share;
|
using IRaCIS.Core.Application.Helper;
|
||||||
|
using IRaCIS.Core.Domain.Models;
|
||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
|
using IRaCIS.Core.Infrastructure.Extention;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Localization;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Localization;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace IRaCIS.Core.API
|
namespace IRaCIS.Core.API
|
||||||
{
|
{
|
||||||
public static class LocalizationConfig
|
public static class LocalizationConfig
|
||||||
{
|
{
|
||||||
|
|
||||||
public static void UseLocalization(this IApplicationBuilder app)
|
public static async Task UseLocalization(this IApplicationBuilder app,IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
var supportedCultures = new List<CultureInfo>
|
var supportedCultures = new List<CultureInfo>
|
||||||
{
|
{
|
||||||
@@ -31,6 +40,28 @@ namespace IRaCIS.Core.API
|
|||||||
//options.RequestCultureProviders.RemoveAt(1);
|
//options.RequestCultureProviders.RemoveAt(1);
|
||||||
|
|
||||||
app.UseRequestLocalization(options);
|
app.UseRequestLocalization(options);
|
||||||
|
|
||||||
|
//设置国际化I18n
|
||||||
|
var localizer = serviceProvider.GetRequiredService<IStringLocalizer>();
|
||||||
|
I18n.SetLocalizer(localizer);
|
||||||
|
|
||||||
|
//初始化国际化
|
||||||
|
|
||||||
|
var _internationalizationRepository = serviceProvider.GetRequiredService<IRepository<Internationalization>>();
|
||||||
|
|
||||||
|
//查询数据库的数据
|
||||||
|
var toJsonList = await _internationalizationRepository.Where(t => t.InternationalizationType == 1).Select(t => new IRCGlobalInfoDTO()
|
||||||
|
{
|
||||||
|
Code = t.Code,
|
||||||
|
Value = t.Value,
|
||||||
|
ValueCN = t.ValueCN,
|
||||||
|
Description = t.Description
|
||||||
|
}).ToListAsync();
|
||||||
|
|
||||||
|
|
||||||
|
await InternationalizationHelper.BatchAddJsonKeyValueAsync(toJsonList);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Serilog;
|
|
||||||
using Serilog.Context;
|
using Serilog.Context;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
using Hangfire;
|
using IRaCIS.Core.API._PipelineExtensions.Serilog;
|
||||||
using Hangfire.Dashboard;
|
using IRaCIS.Core.Domain.Share;
|
||||||
using IRaCIS.Core.API._PipelineExtensions.Serilog;
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace IRaCIS.Core.API
|
namespace IRaCIS.Core.API
|
||||||
{
|
{
|
||||||
@@ -19,8 +20,66 @@ namespace IRaCIS.Core.API
|
|||||||
app.UseSerilogRequestLogging(opts
|
app.UseSerilogRequestLogging(opts
|
||||||
=>
|
=>
|
||||||
{
|
{
|
||||||
opts.MessageTemplate = "{TokenUserRealName} {TokenUserTypeShortName} {ClientIp} {LocalIP} {Host} {Protocol} {RequestMethod} {RequestPath} {RequestBody} responded {StatusCode} in {Elapsed:0.0000} ms";
|
|
||||||
opts.EnrichDiagnosticContext = SerilogHelper.EnrichFromRequest;
|
opts.MessageTemplate = "{FullName} {UserType} {UserIp} {Host} {RequestMethod} {RequestPath} {RequestBody} responded {StatusCode} in {Elapsed:0.0000} ms";
|
||||||
|
|
||||||
|
opts.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
|
||||||
|
{
|
||||||
|
|
||||||
|
var request = httpContext.Request;
|
||||||
|
|
||||||
|
// Set all the common properties available for every request
|
||||||
|
diagnosticContext.Set("Host", request.Host.Value);
|
||||||
|
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
diagnosticContext.Set("FullName", httpContext?.User?.FindFirst(JwtIRaCISClaimType.RealName)?.Value);
|
||||||
|
|
||||||
|
diagnosticContext.Set("UserType", httpContext?.User?.FindFirst(JwtIRaCISClaimType.UserTypeShortName)?.Value);
|
||||||
|
|
||||||
|
var clientIp = httpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault() ??
|
||||||
|
httpContext.Connection.RemoteIpAddress?.ToString();
|
||||||
|
|
||||||
|
if (clientIp.StartsWith("::ffff:"))
|
||||||
|
{
|
||||||
|
clientIp = clientIp.Substring(7); // 移除前缀
|
||||||
|
}
|
||||||
|
|
||||||
|
diagnosticContext.Set("UserIp", clientIp);
|
||||||
|
|
||||||
|
#region 非必要不记录
|
||||||
|
//diagnosticContext.Set("Protocol", request.Protocol);
|
||||||
|
//diagnosticContext.Set("Scheme", request.Scheme);
|
||||||
|
//// Retrieve the IEndpointFeature selected for the request
|
||||||
|
//var endpoint = httpContext.GetEndpoint();
|
||||||
|
//if (endpoint is object) // endpoint != null
|
||||||
|
//{
|
||||||
|
// diagnosticContext.Set("EndpointName", endpoint.DisplayName);
|
||||||
|
//}
|
||||||
|
// Set the content-type of the Response at this point
|
||||||
|
//diagnosticContext.Set("ContentType", httpContext.Response.ContentType);
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region old 未用
|
||||||
|
//这种获取的Ip不准 配置服务才行
|
||||||
|
//diagnosticContext.Set("RequestIP", httpContext.Connection.RemoteIpAddress.ToString());
|
||||||
|
|
||||||
|
//这种方式可以,但是serilog提供了 就不用了
|
||||||
|
//diagnosticContext.Set("TestIP", httpContext.GetUserIp());
|
||||||
|
|
||||||
|
//这种方式不行 读取的body为空字符串 必须在中间件中读取
|
||||||
|
//diagnosticContext.Set("RequestBody", await ReadRequestBody(httpContext.Request));
|
||||||
|
//diagnosticContext.Set("RequestBody", RequestPayload);
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
using IRaCIS.Core.Domain.Share;
|
|
||||||
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);
|
|
||||||
|
|
||||||
diagnosticContext.Set("Protocol", request.Protocol);
|
|
||||||
diagnosticContext.Set("Scheme", request.Scheme);
|
|
||||||
|
|
||||||
#region old 未用
|
|
||||||
//这种获取的Ip不准 配置服务才行
|
|
||||||
//diagnosticContext.Set("RequestIP", httpContext.Connection.RemoteIpAddress.ToString());
|
|
||||||
|
|
||||||
//这种方式可以,但是serilog提供了 就不用了
|
|
||||||
//diagnosticContext.Set("TestIP", httpContext.GetUserIp());
|
|
||||||
|
|
||||||
//这种方式不行 读取的body为空字符串 必须在中间件中读取
|
|
||||||
//diagnosticContext.Set("RequestBody", await ReadRequestBody(httpContext.Request));
|
|
||||||
//diagnosticContext.Set("RequestBody", RequestPayload);
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
// 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(JwtIRaCISClaimType.RealName)?.Value);
|
|
||||||
|
|
||||||
diagnosticContext.Set("TokenUserTypeShortName", httpContext?.User?.FindFirst(JwtIRaCISClaimType.UserTypeShortName)?.Value);
|
|
||||||
|
|
||||||
// Retrieve the IEndpointFeature selected for the request
|
|
||||||
var endpoint = httpContext.GetEndpoint();
|
|
||||||
if (endpoint is object) // endpoint != null
|
|
||||||
{
|
|
||||||
diagnosticContext.Set("EndpointName", endpoint.DisplayName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
using Autofac;
|
|
||||||
using IRaCIS.Core.Application;
|
|
||||||
using IRaCIS.Core.Application.BackGroundJob;
|
|
||||||
using IRaCIS.Core.Infra.EFCore;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Panda.DynamicWebApi;
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using IRaCIS.Core.Domain.Models;
|
|
||||||
using IRaCIS.Core.Domain.Share;
|
|
||||||
using IRaCIS.Core.Application.Service;
|
|
||||||
using IRaCIS.Application.Interfaces;
|
|
||||||
using AutoMapper;
|
|
||||||
|
|
||||||
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.RegisterType<Repository>().As<IRepository>().InstancePerLifetimeScope();
|
|
||||||
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 指定控制器也由autofac 来进行实例获取 https://www.cnblogs.com/xwhqwer/p/15320838.html
|
|
||||||
|
|
||||||
//获取所有控制器类型并使用属性注入
|
|
||||||
containerBuilder.RegisterAssemblyTypes(typeof(BaseService).Assembly)
|
|
||||||
.Where(type => typeof(IDynamicWebApi).IsAssignableFrom(type))
|
|
||||||
.PropertiesAutowired();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Assembly application = Assembly.LoadFrom(AppDomain.CurrentDomain.BaseDirectory + "IRaCIS.Core.Application.dll");
|
|
||||||
containerBuilder.RegisterAssemblyTypes(application).Where(t => t.FullName.Contains("Service"))
|
|
||||||
.PropertiesAutowired().AsImplementedInterfaces();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//containerBuilder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>().SingleInstance();
|
|
||||||
//containerBuilder.RegisterType<UserInfo>().As<IUserInfo>().InstancePerLifetimeScope();
|
|
||||||
|
|
||||||
|
|
||||||
//注册hangfire任务 依赖注入
|
|
||||||
containerBuilder.RegisterType<ObtainTaskAutoCancelJob>().As<IObtainTaskAutoCancelJob>().InstancePerDependency();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,14 @@
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using IRaCIS.Core.Application.BusinessFilter;
|
||||||
|
using IRaCIS.Core.Application.Service.BusinessFilter;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Panda.DynamicWebApi;
|
using Panda.DynamicWebApi;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace IRaCIS.Core.API
|
namespace IRaCIS.Core.API
|
||||||
{
|
{
|
||||||
public static class DynamicWebApiSetup
|
public static class WebApiSetup
|
||||||
{
|
{
|
||||||
//20210910 避免冗余的控制器层代码编写,仅仅包了一层前后台定义的格式 这里采用动态webAPi+IResultFilter 替代大部分情况
|
//20210910 避免冗余的控制器层代码编写,仅仅包了一层前后台定义的格式 这里采用动态webAPi+IResultFilter 替代大部分情况
|
||||||
public static void AddDynamicWebApiSetup(this IServiceCollection services)
|
public static void AddDynamicWebApiSetup(this IServiceCollection services)
|
||||||
@@ -20,5 +25,38 @@ namespace IRaCIS.Core.API
|
|||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void AddMasaMinimalAPiSetUp(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddMasaMinimalAPIs(options =>
|
||||||
|
{
|
||||||
|
options.Prefix = "";//自定义前缀 默认是api
|
||||||
|
options.Version = ""; //默认是V1
|
||||||
|
options.AutoAppendId = false; //路由是否自动附加参数Id 默认是true
|
||||||
|
options.PluralizeServiceName = false; //服务名称是否启用复数
|
||||||
|
|
||||||
|
//options.Assemblies = new List<Assembly>() { typeof(UserSiteSurveySubmitedEventConsumer).Assembly };
|
||||||
|
|
||||||
|
options.GetPrefixes = new List<string> { "Get", "Select", "Find" };
|
||||||
|
options.PostPrefixes = new List<string> { "Post", "Add", "Create", "List" };
|
||||||
|
options.PutPrefixes = new List<string> { "Put", "Update" };
|
||||||
|
options.DeletePrefixes = new List<string> { "Delete", "Remove" };
|
||||||
|
|
||||||
|
options.RouteHandlerBuilder = t => {
|
||||||
|
t.RequireAuthorization()
|
||||||
|
.AddEndpointFilter<LimitUserRequestAuthorizationEndpointFilter>()
|
||||||
|
.AddEndpointFilter<TrialGlobalLimitEndpointFilter>()
|
||||||
|
//.AddEndpointFilter<ModelValidationEndpointFilter>()
|
||||||
|
.AddEndpointFilter<UnifiedApiResultEndpointFilter>()
|
||||||
|
.WithGroupName("Institution").DisableAntiforgery();
|
||||||
|
};
|
||||||
|
options.MapHttpMethodsForUnmatched = new string[] { "Post" };
|
||||||
|
options.DisableTrimMethodPrefix = true; //禁用去除方法前缀
|
||||||
|
options.DisableAutoMapRoute = false;//可通过配置true禁用全局自动路由映射或者删除此配置以启用全局自动路由映射
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using EntityFramework.Exceptions.SqlServer;
|
using EntityFramework.Exceptions.SqlServer;
|
||||||
using Hangfire.SqlServer;
|
|
||||||
using IRaCIS.Core.Application.Triggers;
|
using IRaCIS.Core.Application.Triggers;
|
||||||
using IRaCIS.Core.Domain.Share;
|
using IRaCIS.Core.Domain.Share;
|
||||||
using IRaCIS.Core.Infra.EFCore;
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
@@ -12,8 +11,6 @@ using Microsoft.EntityFrameworkCore.Migrations;
|
|||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using StackExchange.Redis;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.API
|
namespace IRaCIS.Core.API
|
||||||
{
|
{
|
||||||
@@ -54,7 +51,6 @@ namespace IRaCIS.Core.API
|
|||||||
//迁移的时候,不生成外键
|
//迁移的时候,不生成外键
|
||||||
options.ReplaceService<IMigrationsSqlGenerator, NoForeignKeyMigrationsSqlGenerator>();
|
options.ReplaceService<IMigrationsSqlGenerator, NoForeignKeyMigrationsSqlGenerator>();
|
||||||
|
|
||||||
|
|
||||||
options.UseLoggerFactory(logFactory);
|
options.UseLoggerFactory(logFactory);
|
||||||
|
|
||||||
options.UseExceptionProcessor();
|
options.UseExceptionProcessor();
|
||||||
@@ -66,10 +62,6 @@ namespace IRaCIS.Core.API
|
|||||||
|
|
||||||
options.UseProjectables();
|
options.UseProjectables();
|
||||||
|
|
||||||
//options.AddInterceptors(new AuditingInterceptor(configuration.GetSection("ConnectionStrings:RemoteNew").Value));
|
|
||||||
|
|
||||||
//options.UseTriggers(triggerOptions => triggerOptions.AddTrigger<SubjectVisitImageDateTrigger>());
|
|
||||||
|
|
||||||
//options.UseTriggers(triggerOptions => triggerOptions.AddAssemblyTriggers(typeof(SubjectVisitTrigger).Assembly));
|
//options.UseTriggers(triggerOptions => triggerOptions.AddAssemblyTriggers(typeof(SubjectVisitTrigger).Assembly));
|
||||||
|
|
||||||
|
|
||||||
@@ -87,13 +79,7 @@ namespace IRaCIS.Core.API
|
|||||||
|
|
||||||
triggerOptions.AddTrigger<UserLogTrigger>();
|
triggerOptions.AddTrigger<UserLogTrigger>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Register an additional context factory as a Scoped service, which gets a pooled context from the Singleton factory we registered above,
|
// Register an additional context factory as a Scoped service, which gets a pooled context from the Singleton factory we registered above,
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.API
|
|
||||||
{
|
|
||||||
public static class EasyCachingSetup
|
|
||||||
{
|
|
||||||
public static void AddEasyCachingSetup(this IServiceCollection services, IConfiguration configuration)
|
|
||||||
{
|
|
||||||
//services.AddEasyCaching(options =>
|
|
||||||
//{
|
|
||||||
// options.UseInMemory();
|
|
||||||
|
|
||||||
// //options.UseRedis(configuration, EasyCachingConstValue.DefaultRedisName).WithMessagePack(EasyCachingConstValue.DefaultRedisName);
|
|
||||||
|
|
||||||
|
|
||||||
//});
|
|
||||||
|
|
||||||
////services.ConfigureCastleInterceptor(options => options.CacheProviderName = EasyCachingConstValue.DefaultRedisName);
|
|
||||||
|
|
||||||
//services.ConfigureCastleInterceptor(options => options.CacheProviderName = EasyCachingConstValue.DefaultInMemoryName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
using AspNetCoreRateLimit;
|
using AspNetCoreRateLimit;
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.API
|
namespace IRaCIS.Core.API
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
using IRaCIS.Core.Domain.Share;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.API
|
|
||||||
{
|
|
||||||
public static class JsonConfigSetup
|
|
||||||
{
|
|
||||||
public static void AddJsonConfigSetup(this IServiceCollection services, IConfiguration configuration)
|
|
||||||
{
|
|
||||||
|
|
||||||
services.Configure<ServiceVerifyConfigOption>(configuration.GetSection("BasicSystemConfig"));
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
|
|
||||||
using LogDashboard;
|
using LogDashboard;
|
||||||
using LogDashboard.Authorization.Filters;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace IRaCIS.Core.API
|
namespace IRaCIS.Core.API
|
||||||
|
|||||||
@@ -0,0 +1,120 @@
|
|||||||
|
using IRaCIS.Core.API.HostService;
|
||||||
|
using IRaCIS.Core.Application.MassTransit.Consumer;
|
||||||
|
using IRaCIS.Core.Domain.BaseModel;
|
||||||
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
|
using MassTransit;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.API
|
||||||
|
{
|
||||||
|
public static class MassTransitSetup
|
||||||
|
{
|
||||||
|
public static void AddMassTransitSetup(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
|
||||||
|
#region MassTransit
|
||||||
|
//masstransit组件 也支持MediatR 中介者模式,但是支持分布式,考虑后续,所以在次替代MediatR
|
||||||
|
//参考链接:https://masstransit.io/documentation/concepts/mediator#scoped-mediator
|
||||||
|
services.AddMediator(cfg =>
|
||||||
|
{
|
||||||
|
cfg.AddConsumers(typeof(UserSiteSurveySubmitedEventConsumer).Assembly);
|
||||||
|
|
||||||
|
//cfg.AddConsumer<ConsistencyCheckConsumer>();
|
||||||
|
//cfg.AddConsumer<AddSubjectTriggerConsumer>();
|
||||||
|
//cfg.AddConsumer<AddSubjectTriggerConsumer2>();
|
||||||
|
//cfg.ConfigureMediator((context, cfg) => cfg.UseHttpContextScopeFilter(context));
|
||||||
|
});
|
||||||
|
|
||||||
|
//添加 MassTransit 和 InMemory 传输
|
||||||
|
services.AddMassTransit(cfg =>
|
||||||
|
{
|
||||||
|
cfg.AddConsumers(typeof(UserSiteSurveySubmitedEventConsumer).Assembly);
|
||||||
|
|
||||||
|
cfg.AddPublishMessageScheduler();
|
||||||
|
cfg.AddHangfireConsumers();
|
||||||
|
|
||||||
|
// 使用 InMemory 作为消息传递机制
|
||||||
|
cfg.UsingInMemory((context, cfg) =>
|
||||||
|
{
|
||||||
|
cfg.UsePublishMessageScheduler();
|
||||||
|
|
||||||
|
|
||||||
|
cfg.UseConsumeFilter(typeof(ConsumeExceptionFilter<>), context,
|
||||||
|
x => x.Include(type => type.IsAssignableTo(typeof(DomainEvent))));
|
||||||
|
|
||||||
|
cfg.UseConsumeFilter(typeof(CultureInfoFilter<>), context,
|
||||||
|
x => x.Include(type => type.IsAssignableTo(typeof(DomainEvent))));
|
||||||
|
|
||||||
|
cfg.ConfigureEndpoints(context); // 自动配置所有消费者的端点
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
#region rabitmq obsolute
|
||||||
|
|
||||||
|
//cfg.UsingRabbitMq((context, cfg) =>
|
||||||
|
//{
|
||||||
|
// cfg.UsePublishMessageScheduler();
|
||||||
|
|
||||||
|
// cfg.Host(
|
||||||
|
// host: "106.14.89.110",
|
||||||
|
// port: 5672,
|
||||||
|
// virtualHost: "/",
|
||||||
|
// configure: hostConfig =>
|
||||||
|
// {
|
||||||
|
// hostConfig.Username("rabbitmq");
|
||||||
|
// hostConfig.Password("rabbitmq");
|
||||||
|
// });
|
||||||
|
|
||||||
|
// cfg.ConfigureEndpoints(context);
|
||||||
|
//});
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Outbox obsolute
|
||||||
|
|
||||||
|
//cfg.AddConfigureEndpointsCallback((context, name, cfg) =>
|
||||||
|
//{
|
||||||
|
// cfg.UseEntityFrameworkOutbox<IRaCISDBContext>(context);
|
||||||
|
|
||||||
|
// //cfg.UseDelayedRedelivery(r => r.Intervals(TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(15), TimeSpan.FromMinutes(30)));
|
||||||
|
|
||||||
|
// //// 全局重试策略:重试 3 次,每次延迟 5 秒
|
||||||
|
// //cfg.UseMessageRetry(retryConfig =>
|
||||||
|
// //{
|
||||||
|
// // retryConfig.Interval(3, TimeSpan.FromSeconds(10));
|
||||||
|
// //});
|
||||||
|
//});
|
||||||
|
|
||||||
|
//cfg.AddEntityFrameworkOutbox<IRaCISDBContext>(o =>
|
||||||
|
//{
|
||||||
|
// o.UseSqlServer();
|
||||||
|
// o.UseBusOutbox();
|
||||||
|
//});
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//services.AddOptions<MassTransitHostOptions>()
|
||||||
|
// .Configure(options =>
|
||||||
|
// {
|
||||||
|
// options.WaitUntilStarted = true;
|
||||||
|
// options.StartTimeout = TimeSpan.FromMinutes(1);
|
||||||
|
// options.StopTimeout = TimeSpan.FromMinutes(1);
|
||||||
|
// });
|
||||||
|
|
||||||
|
//services.AddOptions<HostOptions>()
|
||||||
|
// .Configure(options => options.ShutdownTimeout = TimeSpan.FromMinutes(1));
|
||||||
|
|
||||||
|
//services.AddHostedService<RecurringJobConfigurationService>();
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Converters;
|
using Newtonsoft.Json.Converters;
|
||||||
using StackExchange.Redis;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
|
||||||
@@ -125,4 +124,75 @@ namespace IRaCIS.Core.API
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region 废弃
|
||||||
|
|
||||||
|
public class MyDateTimeConverter : JsonConverter<DateTime>
|
||||||
|
{
|
||||||
|
public override DateTime ReadJson(JsonReader reader, Type objectType, DateTime existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
return reader.ReadAsDateTime().Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void WriteJson(JsonWriter writer, DateTime value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||||
|
|
||||||
|
string dateFormat;
|
||||||
|
if (!isEn_US)
|
||||||
|
{
|
||||||
|
// Chinese date format
|
||||||
|
dateFormat = "yyyy-MM-dd HH:mm:ss";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Default or English date format
|
||||||
|
dateFormat = "MM/dd/yyyy HH:mm:ss";
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteValue(value.ToString(dateFormat));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MyNullableDateTimeConverter : JsonConverter<DateTime?>
|
||||||
|
{
|
||||||
|
public override DateTime? ReadJson(JsonReader reader, Type objectType, DateTime? existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
var val = reader.ReadAsDateTime();
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void WriteJson(JsonWriter writer, DateTime? value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||||
|
|
||||||
|
string dateFormat;
|
||||||
|
if (!isEn_US)
|
||||||
|
{
|
||||||
|
// Chinese date format
|
||||||
|
dateFormat = "yyyy-MM-dd HH:mm:ss";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Default or English date format
|
||||||
|
dateFormat = "MM/dd/yyyy HH:mm:ss";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.HasValue)
|
||||||
|
{
|
||||||
|
writer.WriteValue(value.Value.ToString(dateFormat));
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writer.WriteValue(default(DateTime?));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,99 +0,0 @@
|
|||||||
using IRaCIS.Core.Domain.Share;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Serialization;
|
|
||||||
using System;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.API
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
//public class CustomContractResolver : DefaultContractResolver
|
|
||||||
//{
|
|
||||||
// protected override JsonContract CreateContract(Type objectType)
|
|
||||||
// {
|
|
||||||
// var contract = base.CreateContract(objectType);
|
|
||||||
|
|
||||||
// // 检查类是否有 LowercaseJsonAttribute 标记
|
|
||||||
// if (objectType.GetCustomAttribute<LowerCamelCaseJsonAttribute>() != null)
|
|
||||||
// {
|
|
||||||
// contract.NamingStrategy = new IRCCamelCaseNamingStrategy();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return contract;
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#region 废弃
|
|
||||||
|
|
||||||
public class MyDateTimeConverter : JsonConverter<DateTime>
|
|
||||||
{
|
|
||||||
public override DateTime ReadJson(JsonReader reader, Type objectType, DateTime existingValue, bool hasExistingValue, JsonSerializer serializer)
|
|
||||||
{
|
|
||||||
return reader.ReadAsDateTime().Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void WriteJson(JsonWriter writer, DateTime value, JsonSerializer serializer)
|
|
||||||
{
|
|
||||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
|
||||||
|
|
||||||
string dateFormat;
|
|
||||||
if (!isEn_US)
|
|
||||||
{
|
|
||||||
// Chinese date format
|
|
||||||
dateFormat = "yyyy-MM-dd HH:mm:ss";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Default or English date format
|
|
||||||
dateFormat = "MM/dd/yyyy HH:mm:ss";
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.WriteValue(value.ToString(dateFormat));
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class MyNullableDateTimeConverter : JsonConverter<DateTime?>
|
|
||||||
{
|
|
||||||
public override DateTime? ReadJson(JsonReader reader, Type objectType, DateTime? existingValue, bool hasExistingValue, JsonSerializer serializer)
|
|
||||||
{
|
|
||||||
var val = reader.ReadAsDateTime();
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void WriteJson(JsonWriter writer, DateTime? value, JsonSerializer serializer)
|
|
||||||
{
|
|
||||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
|
||||||
|
|
||||||
string dateFormat;
|
|
||||||
if (!isEn_US)
|
|
||||||
{
|
|
||||||
// Chinese date format
|
|
||||||
dateFormat = "yyyy-MM-dd HH:mm:ss";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Default or English date format
|
|
||||||
dateFormat = "MM/dd/yyyy HH:mm:ss";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.HasValue)
|
|
||||||
{
|
|
||||||
writer.WriteValue(value.Value.ToString(dateFormat));
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
writer.WriteValue(default(DateTime?));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
@@ -1,16 +1,9 @@
|
|||||||
|
|
||||||
using IRaCIS.Core.API._ServiceExtensions.NewtonsoftJson;
|
using IRaCIS.Core.API._ServiceExtensions.NewtonsoftJson;
|
||||||
using IRaCIS.Core.Application.Helper;
|
using IRaCIS.Core.Application.Helper;
|
||||||
using IRaCIS.Core.Domain.Share;
|
|
||||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Converters;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
|
||||||
using System.Text.Json;
|
|
||||||
using Newtonsoft.Json.Serialization;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.API
|
namespace IRaCIS.Core.API
|
||||||
{
|
{
|
||||||
@@ -29,13 +22,6 @@ namespace IRaCIS.Core.API
|
|||||||
builder.AddNewtonsoftJson(options =>
|
builder.AddNewtonsoftJson(options =>
|
||||||
{
|
{
|
||||||
|
|
||||||
//大驼峰
|
|
||||||
//options.SerializerSettings.ContractResolver = new DefaultContractResolver();
|
|
||||||
//小驼峰
|
|
||||||
//options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 忽略循环引用
|
// 忽略循环引用
|
||||||
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
|
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
|
||||||
|
|
||||||
@@ -47,10 +33,18 @@ namespace IRaCIS.Core.API
|
|||||||
|
|
||||||
options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind;
|
options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind;
|
||||||
|
|
||||||
|
#region 废弃
|
||||||
|
//大驼峰
|
||||||
|
//options.SerializerSettings.ContractResolver = new DefaultContractResolver();
|
||||||
|
//小驼峰
|
||||||
|
//options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//二者只能取其一
|
//二者只能取其一
|
||||||
//options.SerializerSettings.Converters.Add(new MyDateTimeConverter());
|
//options.SerializerSettings.Converters.Add(new MyDateTimeConverter());
|
||||||
//options.SerializerSettings.Converters.Add(new MyNullableDateTimeConverter());
|
//options.SerializerSettings.Converters.Add(new MyNullableDateTimeConverter());
|
||||||
|
#endregion
|
||||||
|
|
||||||
//必须放在后面
|
//必须放在后面
|
||||||
options.SerializerSettings.Converters.Add(services.BuildServiceProvider().GetService<JSONTimeZoneConverter>());
|
options.SerializerSettings.Converters.Add(services.BuildServiceProvider().GetService<JSONTimeZoneConverter>());
|
||||||
@@ -62,6 +56,7 @@ namespace IRaCIS.Core.API
|
|||||||
{
|
{
|
||||||
o.SuppressModelStateInvalidFilter = true; //自己写验证
|
o.SuppressModelStateInvalidFilter = true; //自己写验证
|
||||||
|
|
||||||
|
#region 废弃验证
|
||||||
////这里是自定义验证结果和返回状态码 因为这里是在[ApiController]控制器层校验,动态webApi的不会校验 所以需要单独写一个Filter
|
////这里是自定义验证结果和返回状态码 因为这里是在[ApiController]控制器层校验,动态webApi的不会校验 所以需要单独写一个Filter
|
||||||
//o.InvalidModelStateResponseFactory = (context) =>
|
//o.InvalidModelStateResponseFactory = (context) =>
|
||||||
//{
|
//{
|
||||||
@@ -72,7 +67,7 @@ namespace IRaCIS.Core.API
|
|||||||
|
|
||||||
//return new JsonResult(ResponseOutput.NotOk("The inputs supplied to the API are invalid. " + JsonConvert.SerializeObject( error)));
|
//return new JsonResult(ResponseOutput.NotOk("The inputs supplied to the API are invalid. " + JsonConvert.SerializeObject( error)));
|
||||||
//};
|
//};
|
||||||
|
#endregion
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,27 +8,11 @@ using System.Reflection;
|
|||||||
|
|
||||||
namespace IRaCIS.Core.API
|
namespace IRaCIS.Core.API
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// LowerCamelCaseJsonAttribute 可以设置类小写返回给前端
|
||||||
|
/// </summary>
|
||||||
public class NullToEmptyStringResolver : DefaultContractResolver
|
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)
|
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
|
||||||
{
|
{
|
||||||
@@ -65,4 +49,37 @@ 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,5 @@
|
|||||||
using IRaCIS.Core.Application.Helper;
|
using IRaCIS.Core.Application.Helper;
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Converters;
|
|
||||||
using Newtonsoft.Json.Serialization;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
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;
|
|
||||||
using IRaCIS.Core.Domain.Share;
|
|
||||||
|
|
||||||
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("LocalIP", httpContext.Connection.LocalIpAddress.MapToIPv4().ToString()));
|
|
||||||
|
|
||||||
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("TokenUserRealName", httpContext?.User?.FindFirst(ClaimAttributes.RealName)?.Value));
|
|
||||||
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("TokenUserType", httpContext?.User?.FindFirst(JwtIRaCISClaimType.UserTypeShortName)?.Value));
|
|
||||||
|
|
||||||
|
|
||||||
//这样读取没用
|
|
||||||
//logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("RequestBody", await ReadRequestBody(httpContext.Request)));
|
|
||||||
//logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("RequestIP", IPHelper.GetIP(httpContext.Request) ));
|
|
||||||
//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}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
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.FromLogContext()
|
|
||||||
.Filter.ByExcluding(logEvent =>logEvent.Properties.ContainsKey("RequestPath") && logEvent.Properties["RequestPath"].ToString().Contains("/health"))
|
|
||||||
|
|
||||||
//控制台 方便调试 问题 我们显示记录日志 时 获取上下文的ip 和用户名 用户类型
|
|
||||||
.WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Warning,
|
|
||||||
outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {SourceContext:l} || {Message} || {Exception} ||end {NewLine}")
|
|
||||||
|
|
||||||
.WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day,
|
|
||||||
outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {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 = "test@extimaging.com",//发件人邮箱
|
|
||||||
// MailServer = "smtp.qiye.aliyun.com",//smtp服务器地址
|
|
||||||
// NetworkCredentials = new NetworkCredential("test@extimaging.com", "SHzyyl2021"),//两个参数分别是发件人邮箱与客户端授权码
|
|
||||||
// Port = 465,//端口号
|
|
||||||
// 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,76 @@
|
|||||||
|
using Amazon.SecurityToken.Model;
|
||||||
|
using DocumentFormat.OpenXml.Bibliography;
|
||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using Microsoft.Extensions.Configuration.Json;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Serilog;
|
||||||
|
using Serilog.Events;
|
||||||
|
using Serilog.Formatting.Compact;
|
||||||
|
using Serilog.Formatting.Display;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.API
|
||||||
|
{
|
||||||
|
public class SerilogExtension
|
||||||
|
{
|
||||||
|
|
||||||
|
public static void AddSerilogSetup(string environment)
|
||||||
|
{
|
||||||
|
|
||||||
|
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("Microsoft.AspNetCore.Hosting", LogEventLevel.Warning)
|
||||||
|
.MinimumLevel.Override("Microsoft.AspNetCore.Mvc", LogEventLevel.Warning)
|
||||||
|
.MinimumLevel.Override("Microsoft.AspNetCore.Routing", LogEventLevel.Warning)
|
||||||
|
.MinimumLevel.Override("Hangfire", LogEventLevel.Warning)
|
||||||
|
.Enrich.FromLogContext()
|
||||||
|
.Filter.ByExcluding(logEvent => logEvent.Properties.ContainsKey("RequestPath") && logEvent.Properties["RequestPath"].ToString().Contains("/health"))
|
||||||
|
.WriteTo.Console()
|
||||||
|
.WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day);
|
||||||
|
|
||||||
|
#region 根据环境配置是否打开错误发送邮件通知
|
||||||
|
|
||||||
|
//读取配置文件
|
||||||
|
var configuration = new ConfigurationBuilder().Add(new JsonConfigurationSource { Path = $"appsettings.{environment}.json", ReloadOnChange = true }).Build();
|
||||||
|
|
||||||
|
// 手动绑定配置
|
||||||
|
var emailConfig = new SystemEmailSendConfig();
|
||||||
|
configuration.GetSection("SystemEmailSendConfig").Bind(emailConfig);
|
||||||
|
|
||||||
|
if (emailConfig.IsOpenErrorNoticeEmail)
|
||||||
|
{
|
||||||
|
config.WriteTo.Email(options: new Serilog.Sinks.Email.EmailSinkOptions()
|
||||||
|
{
|
||||||
|
From = emailConfig.FromEmail,
|
||||||
|
To = emailConfig.ErrorNoticeEmailList,
|
||||||
|
Host = emailConfig.Host,
|
||||||
|
Port = emailConfig.Port,
|
||||||
|
Subject = new MessageTemplateTextFormatter("Log Alert - 系统发生了异常,请核查"),
|
||||||
|
Credentials = new NetworkCredential(emailConfig.FromEmail, emailConfig.AuthorizationCode)
|
||||||
|
|
||||||
|
}, restrictedToMinimumLevel: Serilog.Events.LogEventLevel.Error);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
Log.Logger = config.CreateLogger();
|
||||||
|
|
||||||
|
#region 废弃-输出Json格式的日志
|
||||||
|
//如果有反向代理并不会获取到用户的真实IP
|
||||||
|
//.Enrich.WithClientIp()
|
||||||
|
//.Enrich.WithRequestHeader("User-Agent")
|
||||||
|
//https://github.com/serilog/serilog-formatting-compact
|
||||||
|
//// 控制台输出 JSON 格式
|
||||||
|
//.WriteTo.Console(formatter: new CompactJsonFormatter(), LogEventLevel.Warning),
|
||||||
|
//// 文件输出 JSON 格式
|
||||||
|
//.WriteTo.File(new CompactJsonFormatter(), $"{AppContext.BaseDirectory}Serilogs/.json", rollingInterval: RollingInterval.Day);
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
using IP2Region.Net.Abstractions;
|
||||||
|
using IP2Region.Net.XDB;
|
||||||
|
using IRaCIS.Core.Application.BackGroundJob;
|
||||||
|
using IRaCIS.Core.Application.Helper;
|
||||||
|
using IRaCIS.Core.Application.Service;
|
||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.HttpOverrides;
|
||||||
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Panda.DynamicWebApi;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.API;
|
||||||
|
|
||||||
|
|
||||||
|
public static class ServiceCollectionSetup
|
||||||
|
{
|
||||||
|
public static void ConfigureServices(this IServiceCollection services, IConfiguration _configuration)
|
||||||
|
{
|
||||||
|
services.AddOptions().Configure<SystemEmailSendConfig>(_configuration.GetSection("SystemEmailSendConfig"));
|
||||||
|
services.AddOptions().Configure<ServiceVerifyConfigOption>(_configuration.GetSection("BasicSystemConfig"));
|
||||||
|
services.AddOptions().Configure<AliyunOSSOptions>(_configuration.GetSection("AliyunOSS"));
|
||||||
|
services.AddOptions().Configure<ObjectStoreServiceOptions>(_configuration.GetSection("ObjectStoreService"));
|
||||||
|
services.AddOptions().Configure<IRCEncreptOption>(_configuration.GetSection("EncrypteResponseConfig"));
|
||||||
|
services.AddOptions().Configure<SystemPacsConfig>(_configuration.GetSection("SystemPacsConfig"));
|
||||||
|
services.Configure<IRaCISBasicConfigOption>(_configuration.GetSection("IRaCISBasicConfig"));
|
||||||
|
|
||||||
|
services.Configure<ServiceVerifyConfigOption>(_configuration.GetSection("BasicSystemConfig"));
|
||||||
|
|
||||||
|
//转发头设置 获取真实IP
|
||||||
|
services.Configure<ForwardedHeadersOptions>(options =>
|
||||||
|
{
|
||||||
|
options.ForwardedHeaders =
|
||||||
|
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
|
||||||
|
});
|
||||||
|
|
||||||
|
services.AddSingleton<IUserIdProvider, IRaCISUserIdProvider>();
|
||||||
|
services.AddSingleton<ISearcher>(new Searcher(CachePolicy.Content, Path.Combine(AppContext.BaseDirectory, StaticData.Folder.Resources, "ip2region.xdb")));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
|
||||||
|
services.AddScoped<IRepository, Repository>();
|
||||||
|
|
||||||
|
services.AddScoped<IObtainTaskAutoCancelJob, ObtainTaskAutoCancelJob>();
|
||||||
|
|
||||||
|
// 注册以Service 结尾的服务
|
||||||
|
services.Scan(scan => scan
|
||||||
|
.FromAssemblies(typeof(BaseService).Assembly)
|
||||||
|
.AddClasses(classes => classes.Where(t => t.Name.Contains("Service")))
|
||||||
|
.AsImplementedInterfaces()
|
||||||
|
.WithScopedLifetime());
|
||||||
|
|
||||||
|
#region Register Controllers
|
||||||
|
|
||||||
|
// Register all controllers in the assembly that implement IDynamicWebApi
|
||||||
|
services.Scan(scan => scan
|
||||||
|
.FromAssemblies(typeof(BaseService).Assembly)
|
||||||
|
.AddClasses(classes => classes.AssignableTo<IDynamicWebApi>())
|
||||||
|
.AsSelf()
|
||||||
|
.WithScopedLifetime());
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
// MediatR 进程内消息 事件解耦 从程序集中 注册命令和handler对应关系
|
||||||
|
//builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblyContaining<ConsistencyVerificationHandler>());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region 历史废弃配置
|
||||||
|
//builder.Services.AddMemoryCache();
|
||||||
|
////上传限制 配置
|
||||||
|
//builder.Services.Configure<FormOptions>(options =>
|
||||||
|
//{
|
||||||
|
// options.MultipartBodyLengthLimit = int.MaxValue;
|
||||||
|
// options.ValueCountLimit = int.MaxValue;
|
||||||
|
// options.ValueLengthLimit = int.MaxValue;
|
||||||
|
//});
|
||||||
|
//IP 限流 可设置白名单 或者黑名单
|
||||||
|
//services.AddIpPolicyRateLimitSetup(_configuration);
|
||||||
|
// 用户类型 策略授权
|
||||||
|
//services.AddAuthorizationPolicySetup(_configuration);
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Autofac 废弃
|
||||||
|
//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.RegisterType<Repository>().As<IRepository>().InstancePerLifetimeScope();
|
||||||
|
|
||||||
|
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
// #region 指定控制器也由autofac 来进行实例获取 https://www.cnblogs.com/xwhqwer/p/15320838.html
|
||||||
|
|
||||||
|
// //获取所有控制器类型并使用属性注入
|
||||||
|
// containerBuilder.RegisterAssemblyTypes(typeof(BaseService).Assembly)
|
||||||
|
// .Where(type => typeof(IDynamicWebApi).IsAssignableFrom(type))
|
||||||
|
// .PropertiesAutowired();
|
||||||
|
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
|
||||||
|
// Assembly application = Assembly.LoadFrom(AppDomain.CurrentDomain.BaseDirectory + "IRaCIS.Core.Application.dll");
|
||||||
|
// containerBuilder.RegisterAssemblyTypes(application).Where(t => t.FullName.Contains("Service"))
|
||||||
|
// .PropertiesAutowired().AsImplementedInterfaces();
|
||||||
|
|
||||||
|
// //containerBuilder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>().SingleInstance();
|
||||||
|
// //containerBuilder.RegisterType<UserInfo>().As<IUserInfo>().InstancePerLifetimeScope();
|
||||||
|
|
||||||
|
|
||||||
|
// //注册hangfire任务 依赖注入
|
||||||
|
// containerBuilder.RegisterType<ObtainTaskAutoCancelJob>().As<IObtainTaskAutoCancelJob>().InstancePerDependency();
|
||||||
|
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
#endregion
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
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();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,137 +0,0 @@
|
|||||||
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.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.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,128 @@
|
|||||||
|
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.ComponentModel;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.API;
|
||||||
|
|
||||||
|
public enum SwaggerVersion
|
||||||
|
{
|
||||||
|
[Description("医生模块")]
|
||||||
|
Reviewer = 1,
|
||||||
|
[Description("项目模块")]
|
||||||
|
Trial = 2,
|
||||||
|
[Description("入组模块")]
|
||||||
|
Enroll = 3,
|
||||||
|
[Description("工作量模块")]
|
||||||
|
Workload = 4,
|
||||||
|
[Description("通用信息获取")]
|
||||||
|
Common = 5,
|
||||||
|
[Description("机构信息模块")]
|
||||||
|
Institution = 6,
|
||||||
|
[Description("统计模块")]
|
||||||
|
DashboardStatistics = 7,
|
||||||
|
[Description("财务模块")]
|
||||||
|
Financial = 8,
|
||||||
|
[Description("管理模块")]
|
||||||
|
Management =9,
|
||||||
|
[Description("影像模块")]
|
||||||
|
Image =10,
|
||||||
|
[Description("读片模块")]
|
||||||
|
Reading =11
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
public static class SwaggerSetup
|
||||||
|
{
|
||||||
|
public static void AddSwaggerSetup(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddEndpointsApiExplorer();
|
||||||
|
services.AddSwaggerGen(options =>
|
||||||
|
{
|
||||||
|
|
||||||
|
typeof(SwaggerVersion).GetFields(BindingFlags.Public | BindingFlags.Static).ToList()
|
||||||
|
.ForEach(field =>
|
||||||
|
{
|
||||||
|
var description = field.GetCustomAttribute<DescriptionAttribute>()?.Description ?? field.Name;
|
||||||
|
options.SwaggerDoc(field.Name, new Microsoft.OpenApi.Models.OpenApiInfo
|
||||||
|
{
|
||||||
|
Version = field.Name,
|
||||||
|
Description = $"{field.Name} API",
|
||||||
|
Title = description // 使用Description作为Title
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 接口排序
|
||||||
|
options.OrderActionsBy(o => o.GroupName);
|
||||||
|
|
||||||
|
//添加注释
|
||||||
|
var basePath = AppContext.BaseDirectory;
|
||||||
|
var xmlPath1 = Path.Combine(basePath, "IRaCIS.Core.Application.xml");
|
||||||
|
var xmlPath2 = Path.Combine(basePath, "IRaCIS.Core.API.xml");
|
||||||
|
options.IncludeXmlComments(xmlPath1, true);
|
||||||
|
options.IncludeXmlComments(xmlPath2, true);
|
||||||
|
|
||||||
|
// 在header中添加token,传递到后台
|
||||||
|
options.OperationFilter<SecurityRequirementsOperationFilter>();
|
||||||
|
|
||||||
|
|
||||||
|
// 添加登录按钮
|
||||||
|
options.AddSecurityDefinition("bearerAuth", new OpenApiSecurityScheme()
|
||||||
|
{
|
||||||
|
Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
|
||||||
|
Name = "Authorization",
|
||||||
|
//In = "header",
|
||||||
|
//Type = "apiKey"
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||||
|
{
|
||||||
|
app.UseSwagger();
|
||||||
|
app.UseSwaggerUI(options =>
|
||||||
|
{
|
||||||
|
typeof(SwaggerVersion).GetFields(BindingFlags.Public | BindingFlags.Static).ToList()
|
||||||
|
.ForEach(field =>
|
||||||
|
{
|
||||||
|
var description = field.GetCustomAttribute<DescriptionAttribute>()?.Description ?? field.Name;
|
||||||
|
options.SwaggerEndpoint($"swagger/{field.Name}/swagger.json", $"{description}");
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
var data = Assembly.GetExecutingAssembly().Location;
|
||||||
|
options.IndexStream = () => Assembly.GetExecutingAssembly()
|
||||||
|
.GetManifestResourceStream("IRaCIS.Core.API.wwwroot.swagger.ui.Index.html");
|
||||||
|
|
||||||
|
//路径配置,设置为空,表示直接在根域名(localhost:8001)访问该文件,
|
||||||
|
//注意localhost:8001/swagger是访问不到的,去launchSettings.json把launchUrl去掉,如果你想换一个路径,直接写名字即可,比如直接写c.Route = "doc";
|
||||||
|
options.RoutePrefix = string.Empty;
|
||||||
|
options.RoutePrefix = string.Empty;
|
||||||
|
|
||||||
|
//DocExpansion设置为none可折叠所有方法
|
||||||
|
options.DocExpansion(DocExpansion.None);
|
||||||
|
|
||||||
|
//DefaultModelsExpandDepth设置为 - 1 可不显示models
|
||||||
|
options.DefaultModelsExpandDepth(-1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -27,18 +27,10 @@ namespace IRaCIS.Core.API
|
|||||||
hangFireConfig.UseSqlServerStorage(hangFireConnStr, new SqlServerStorageOptions()
|
hangFireConfig.UseSqlServerStorage(hangFireConnStr, new SqlServerStorageOptions()
|
||||||
{
|
{
|
||||||
SchemaName = "dbo",
|
SchemaName = "dbo",
|
||||||
CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
|
}).UseRecommendedSerializerSettings().UseSimpleAssemblyNameTypeSerializer();
|
||||||
SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
|
|
||||||
QueuePollInterval = TimeSpan.Zero,
|
|
||||||
UseRecommendedIsolationLevel = true,
|
|
||||||
DisableGlobalLocks = true
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//hangFireConfig.UseTagsWithSql(); //nuget引入Hangfire.Tags.SqlServer
|
//hangFireConfig.UseTagsWithSql(); //nuget引入Hangfire.Tags.SqlServer
|
||||||
//.UseHangfireHttpJob();
|
//.UseHangfireHttpJob();
|
||||||
|
|
||||||
@@ -46,6 +38,8 @@ namespace IRaCIS.Core.API
|
|||||||
|
|
||||||
services.AddHangfireServer(option =>
|
services.AddHangfireServer(option =>
|
||||||
{
|
{
|
||||||
|
//默认15s检查一次
|
||||||
|
option.SchedulePollingInterval = TimeSpan.FromSeconds(5);
|
||||||
option.Queues = new[] { "immediately_once", "default", "sys_init", "not_immediately_once" };
|
option.Queues = new[] { "immediately_once", "default", "sys_init", "not_immediately_once" };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,7 @@
|
|||||||
"ContinuousReadingTimeMin": 120,
|
"ContinuousReadingTimeMin": 120,
|
||||||
|
|
||||||
"ReadingRestTimeMin": 10,
|
"ReadingRestTimeMin": 10,
|
||||||
|
|
||||||
"IsNeedChangePassWord": true,
|
"IsNeedChangePassWord": true,
|
||||||
|
|
||||||
"ChangePassWordDays": 90
|
"ChangePassWordDays": 90
|
||||||
@@ -69,7 +70,9 @@
|
|||||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||||
"CompanyShortName": "Extensive Imaging",
|
"CompanyShortName": "Extensive Imaging",
|
||||||
"CompanyShortNameCN": "展影医疗",
|
"CompanyShortNameCN": "展影医疗",
|
||||||
"IsEnv_US": false
|
"IsEnv_US": false,
|
||||||
|
"IsOpenErrorNoticeEmail": true,
|
||||||
|
"ErrorNoticeEmailList": [ "872297557@qq.com" ]
|
||||||
},
|
},
|
||||||
"SystemPacsConfig": {
|
"SystemPacsConfig": {
|
||||||
"Port": "11113",
|
"Port": "11113",
|
||||||
|
|||||||
@@ -72,7 +72,9 @@
|
|||||||
"ReadingRestTimeMin": 10,
|
"ReadingRestTimeMin": 10,
|
||||||
"IsNeedChangePassWord": true,
|
"IsNeedChangePassWord": true,
|
||||||
|
|
||||||
"ChangePassWordDays": 90
|
"ChangePassWordDays": 90,
|
||||||
|
|
||||||
|
"ThirdPdfUrl": "http://106.14.89.110:30088/api/v1/convert/file/pdf"
|
||||||
},
|
},
|
||||||
|
|
||||||
"SystemEmailSendConfig": {
|
"SystemEmailSendConfig": {
|
||||||
@@ -89,7 +91,9 @@
|
|||||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||||
"CompanyShortName": "Extensive Imaging",
|
"CompanyShortName": "Extensive Imaging",
|
||||||
"CompanyShortNameCN": "展影医疗",
|
"CompanyShortNameCN": "展影医疗",
|
||||||
"IsEnv_US": false
|
"IsEnv_US": false,
|
||||||
|
"IsOpenErrorNoticeEmail": false,
|
||||||
|
"ErrorNoticeEmailList": ["872297557@qq.com"]
|
||||||
},
|
},
|
||||||
|
|
||||||
"SystemPacsConfig": {
|
"SystemPacsConfig": {
|
||||||
|
|||||||
@@ -74,7 +74,9 @@
|
|||||||
"CompanyShortName": "Elevate Imaging",
|
"CompanyShortName": "Elevate Imaging",
|
||||||
"CompanyShortNameCN": "展影医疗",
|
"CompanyShortNameCN": "展影医疗",
|
||||||
"SiteUrl": "https://lili.elevateimaging.ai/login",
|
"SiteUrl": "https://lili.elevateimaging.ai/login",
|
||||||
"IsEnv_US": true
|
"IsEnv_US": true,
|
||||||
|
"IsOpenErrorNoticeEmail": true,
|
||||||
|
"ErrorNoticeEmailList": [ "872297557@qq.com" ]
|
||||||
},
|
},
|
||||||
|
|
||||||
"SystemPacsConfig": {
|
"SystemPacsConfig": {
|
||||||
|
|||||||
@@ -82,7 +82,9 @@
|
|||||||
"CompanyShortName": "Elevate Imaging",
|
"CompanyShortName": "Elevate Imaging",
|
||||||
"CompanyShortNameCN": "展影医疗",
|
"CompanyShortNameCN": "展影医疗",
|
||||||
"SiteUrl": "https://lili.test.elevateimaging.ai/login",
|
"SiteUrl": "https://lili.test.elevateimaging.ai/login",
|
||||||
"IsEnv_US": true
|
"IsEnv_US": true,
|
||||||
|
"IsOpenErrorNoticeEmail": false,
|
||||||
|
"ErrorNoticeEmailList": [ "872297557@qq.com" ]
|
||||||
},
|
},
|
||||||
|
|
||||||
"SystemPacsConfig": {
|
"SystemPacsConfig": {
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
"AccessKeyId": "AKIAW3MEAFJX7IPXISP4",
|
"AccessKeyId": "AKIAW3MEAFJX7IPXISP4",
|
||||||
"SecretAccessKey": "Pgrg3le5jPxZQ7MR1yYNS30J0XRyJeKVyIIjElXc",
|
"SecretAccessKey": "Pgrg3le5jPxZQ7MR1yYNS30J0XRyJeKVyIIjElXc",
|
||||||
"BucketName": "ei-med-s3-lili-uat-store",
|
"BucketName": "ei-med-s3-lili-uat-store",
|
||||||
"ViewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com/",
|
"ViewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com",
|
||||||
"DurationSeconds": 7200
|
"DurationSeconds": 7200
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -86,7 +86,9 @@
|
|||||||
"CompanyShortName": "Elevate Imaging",
|
"CompanyShortName": "Elevate Imaging",
|
||||||
"CompanyShortNameCN": "展影医疗",
|
"CompanyShortNameCN": "展影医疗",
|
||||||
"SiteUrl": "https://lili.test.elevateimaging.ai/login",
|
"SiteUrl": "https://lili.test.elevateimaging.ai/login",
|
||||||
"IsEnv_US": true
|
"IsEnv_US": true,
|
||||||
|
"IsOpenErrorNoticeEmail": false,
|
||||||
|
"ErrorNoticeEmailList": [ "872297557@qq.com" ]
|
||||||
},
|
},
|
||||||
|
|
||||||
"SystemPacsConfig": {
|
"SystemPacsConfig": {
|
||||||
|
|||||||
@@ -87,7 +87,9 @@
|
|||||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||||
"CompanyShortName": "Extensive Imaging",
|
"CompanyShortName": "Extensive Imaging",
|
||||||
"CompanyShortNameCN": "展影医疗",
|
"CompanyShortNameCN": "展影医疗",
|
||||||
"IsEnv_US": false
|
"IsEnv_US": false,
|
||||||
|
"IsOpenErrorNoticeEmail": false,
|
||||||
|
"ErrorNoticeEmailList": [ "872297557@qq.com" ]
|
||||||
},
|
},
|
||||||
|
|
||||||
"SystemPacsConfig": {
|
"SystemPacsConfig": {
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ namespace IRaCIS.Core.Application.Auth
|
|||||||
|
|
||||||
public bool IsTestUser { get; set; }
|
public bool IsTestUser { get; set; }
|
||||||
|
|
||||||
|
public bool IsZhiZhun { get; set; }
|
||||||
|
|
||||||
public string Phone { get; set; } = String.Empty;
|
public string Phone { get; set; } = String.Empty;
|
||||||
|
|
||||||
public static IRaCISClaims Create(UserBasicInfo user)
|
public static IRaCISClaims Create(UserBasicInfo user)
|
||||||
@@ -36,18 +38,9 @@ namespace IRaCIS.Core.Application.Auth
|
|||||||
IsTestUser = user.IsTestUser,
|
IsTestUser = user.IsTestUser,
|
||||||
Code = user.Code,
|
Code = user.Code,
|
||||||
PermissionStr = user.PermissionStr,
|
PermissionStr = user.PermissionStr,
|
||||||
|
IsZhiZhun = user.IsZhiZhun,
|
||||||
UserTypeShortName = user.UserTypeShortName
|
UserTypeShortName = user.UserTypeShortName
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
public static IRaCISClaims Create(DoctorAccountDTO doctor)
|
|
||||||
{
|
|
||||||
return new IRaCISClaims
|
|
||||||
{
|
|
||||||
Id = doctor.Id,
|
|
||||||
FullName = doctor.FirstName + doctor.LastName,
|
|
||||||
Phone = doctor.Phone,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -36,7 +36,7 @@ namespace IRaCIS.Core.Application.Auth
|
|||||||
new Claim(JwtIRaCISClaimType.UserTypeEnumInt,((int)user.UserTypeEnum).ToString()),
|
new Claim(JwtIRaCISClaimType.UserTypeEnumInt,((int)user.UserTypeEnum).ToString()),
|
||||||
new Claim(JwtIRaCISClaimType.UserTypeShortName,user.UserTypeShortName),
|
new Claim(JwtIRaCISClaimType.UserTypeShortName,user.UserTypeShortName),
|
||||||
new Claim(JwtIRaCISClaimType.PermissionStr,user.PermissionStr),
|
new Claim(JwtIRaCISClaimType.PermissionStr,user.PermissionStr),
|
||||||
|
new Claim(JwtIRaCISClaimType.IsZhiZhun,user.IsZhiZhun.ToString()),
|
||||||
new Claim(JwtIRaCISClaimType.IsTestUser,user.IsTestUser.ToString())
|
new Claim(JwtIRaCISClaimType.IsTestUser,user.IsTestUser.ToString())
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,104 +0,0 @@
|
|||||||
using Hangfire;
|
|
||||||
using Hangfire.Storage;
|
|
||||||
using IRaCIS.Core.Application.Helper;
|
|
||||||
using IRaCIS.Core.Domain.Share;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.Application.Service.BackGroundJob
|
|
||||||
{
|
|
||||||
|
|
||||||
public interface IIRaCISHangfireJob
|
|
||||||
{
|
|
||||||
|
|
||||||
//Task MemoryCacheTrialStatusAsync();
|
|
||||||
|
|
||||||
Task InitHangfireJobTaskAsync();
|
|
||||||
|
|
||||||
}
|
|
||||||
public class IRaCISCHangfireJob(ILogger<IRaCISCHangfireJob> _logger,
|
|
||||||
IRepository<Internationalization> _internationalizationRepository,
|
|
||||||
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigRepository
|
|
||||||
) : IIRaCISHangfireJob
|
|
||||||
{
|
|
||||||
public static string JsonFileFolder = Path.Combine(AppContext.BaseDirectory, StaticData.Folder.Resources);
|
|
||||||
|
|
||||||
|
|
||||||
public async Task InitHangfireJobTaskAsync()
|
|
||||||
{
|
|
||||||
_logger.LogInformation("项目启动 hangfire 任务初始化 执行开始~");
|
|
||||||
|
|
||||||
//初始化国际化
|
|
||||||
|
|
||||||
await InternationalizationHelper.InitInternationlizationDataAndWatchJsonFileAsync(_internationalizationRepository);
|
|
||||||
|
|
||||||
//创建邮件定时任务
|
|
||||||
await InitSysAndTrialCronJobAsync();
|
|
||||||
|
|
||||||
#region 废弃
|
|
||||||
////项目状态 立即加载到缓存中
|
|
||||||
//await MemoryCacheTrialStatusAsync();
|
|
||||||
|
|
||||||
////await MemoryCacheAnonymizeData();
|
|
||||||
|
|
||||||
|
|
||||||
////创建项目缓存 定时任务
|
|
||||||
//HangfireJobHelper.AddOrUpdateInitCronJob<IIRaCISHangfireJob>("RecurringJob_Cache_TrialState", t => t.MemoryCacheTrialStatusAsync(), Cron.Daily());
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
_logger.LogInformation("项目启动 hangfire 任务初始化 执行结束");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public async Task InitSysAndTrialCronJobAsync()
|
|
||||||
{
|
|
||||||
//var deleteJobIdList = await _trialEmailNoticeConfigRepository.Where(t => t.Trial.TrialStatusStr != StaticData.TrialState.TrialOngoing && t.EmailCron != string.Empty && t.IsAutoSend)
|
|
||||||
// .Select(t => t.TrialId + "_" + t.Id)
|
|
||||||
// .ToListAsync();
|
|
||||||
|
|
||||||
//foreach (var jobId in deleteJobIdList)
|
|
||||||
//{
|
|
||||||
// HangfireJobHelper.RemoveCronJob(jobId);
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
|
||||||
var taskInfoList = await _trialEmailNoticeConfigRepository.Where(t => t.Trial.TrialStatusStr == StaticData.TrialState.TrialOngoing && t.EmailCron != string.Empty && t.IsAutoSend)
|
|
||||||
.Select(t => new { t.Id, t.Code, t.EmailCron, t.BusinessScenarioEnum, t.TrialId })
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
foreach (var task in taskInfoList)
|
|
||||||
{
|
|
||||||
//利用主键作为任务Id
|
|
||||||
var jobId = $"{task.TrialId}_{task.Id}";
|
|
||||||
|
|
||||||
HangfireJobHelper.AddOrUpdateTrialCronJob(jobId, task.TrialId, task.BusinessScenarioEnum, task.EmailCron);
|
|
||||||
}
|
|
||||||
|
|
||||||
var addOrUpdateJobIdList = taskInfoList.Select(t => $"{t.TrialId}_{t.Id}").ToList();
|
|
||||||
|
|
||||||
var list = JobStorage.Current.GetConnection().GetRecurringJobs().ToList();
|
|
||||||
|
|
||||||
//项目定时任务都在default 队列
|
|
||||||
//var dbJobIdList = JobStorage.Current.GetConnection().GetRecurringJobs().Where(t => t.Queue == "default").Select(t => t.Id).ToList();
|
|
||||||
|
|
||||||
//var deleteList= dbJobIdList.Except(addOrUpdateJobIdList).ToList();
|
|
||||||
|
|
||||||
// foreach (var jobId in deleteList)
|
|
||||||
// {
|
|
||||||
// HangfireJobHelper.RemoveCronJob(jobId);
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -2,37 +2,24 @@
|
|||||||
using IRaCIS.Core.Application.Service.BusinessFilter;
|
using IRaCIS.Core.Application.Service.BusinessFilter;
|
||||||
using IRaCIS.Core.Domain.Share;
|
using IRaCIS.Core.Domain.Share;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Localization;
|
|
||||||
using Panda.DynamicWebApi;
|
using Panda.DynamicWebApi;
|
||||||
using Panda.DynamicWebApi.Attributes;
|
using Panda.DynamicWebApi.Attributes;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using ZiggyCreatures.Caching.Fusion;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.Application.Service
|
|
||||||
{
|
|
||||||
|
|
||||||
#pragma warning disable CS8618
|
|
||||||
|
|
||||||
|
|
||||||
#region 非泛型版本
|
namespace IRaCIS.Core.Application.Service;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[TypeFilter(typeof(UnifiedApiResultFilter))]
|
[TypeFilter(typeof(UnifiedApiResultFilter))]
|
||||||
[Authorize, DynamicWebApi]
|
[Authorize, DynamicWebApi]
|
||||||
public class BaseService : IBaseService, IDynamicWebApi
|
public class BaseService : IDynamicWebApi
|
||||||
{
|
{
|
||||||
public IMapper _mapper { get; set; }
|
|
||||||
|
|
||||||
public IUserInfo _userInfo { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public IStringLocalizer _localizer { get; set; }
|
|
||||||
|
|
||||||
public IWebHostEnvironment _hostEnvironment { get; set; }
|
|
||||||
|
|
||||||
public IFusionCache _fusionCache { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
public static IResponseOutput Null404NotFound<TEntity>(TEntity? businessObject) where TEntity : class
|
public static IResponseOutput Null404NotFound<TEntity>(TEntity? businessObject) where TEntity : class
|
||||||
@@ -42,76 +29,3 @@ namespace IRaCIS.Core.Application.Service
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public interface IBaseService
|
|
||||||
{
|
|
||||||
[MemberNotNull(nameof(_mapper))]
|
|
||||||
public IMapper _mapper { get; set; }
|
|
||||||
|
|
||||||
[MemberNotNull(nameof(_userInfo))]
|
|
||||||
public IUserInfo _userInfo { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
[MemberNotNull(nameof(_localizer))]
|
|
||||||
public IStringLocalizer _localizer { get; set; }
|
|
||||||
|
|
||||||
[MemberNotNull(nameof(_hostEnvironment))]
|
|
||||||
public IWebHostEnvironment _hostEnvironment { get; set; }
|
|
||||||
|
|
||||||
[MemberNotNull(nameof(_fusionCache))]
|
|
||||||
public IFusionCache _fusionCache { 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(_localizer))]
|
|
||||||
public IStringLocalizer _localizer { get; set; }
|
|
||||||
|
|
||||||
[MemberNotNull(nameof(_fusionCache))]
|
|
||||||
public IFusionCache _fusionCache { get; set; }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
[TypeFilter(typeof(UnifiedApiResultFilter))]
|
|
||||||
[Authorize, DynamicWebApi]
|
|
||||||
public class BaseServiceTest<T> : IBaseServiceTest<T>, IDynamicWebApi where T : Entity
|
|
||||||
{
|
|
||||||
public IMapper _mapper { get; set; }
|
|
||||||
|
|
||||||
public IUserInfo _userInfo { get; set; }
|
|
||||||
|
|
||||||
public IStringLocalizer _localizer { get; set; }
|
|
||||||
|
|
||||||
public IFusionCache _fusionCache { 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
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Diagnostics;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.Application.BusinessFilter;
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 不生效,不知道为啥
|
|
||||||
/// </summary>
|
|
||||||
public class GlobalExceptionHandler : IExceptionHandler
|
|
||||||
{
|
|
||||||
private readonly ILogger<GlobalExceptionHandler> _logger;
|
|
||||||
public GlobalExceptionHandler(ILogger<GlobalExceptionHandler> logger)
|
|
||||||
{
|
|
||||||
this._logger = logger;
|
|
||||||
}
|
|
||||||
public ValueTask<bool> TryHandleAsync(
|
|
||||||
HttpContext httpContext,
|
|
||||||
Exception exception,
|
|
||||||
CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
httpContext.Response.ContentType = "application/json";
|
|
||||||
|
|
||||||
|
|
||||||
var ex = exception;
|
|
||||||
var errorInfo = $"Exception: {ex.Message}[{ex.StackTrace}]" + (ex.InnerException != null ? $" InnerException: {ex.InnerException.Message}[{ex.InnerException.StackTrace}]" : "");
|
|
||||||
|
|
||||||
httpContext.Response.WriteAsJsonAsync(ResponseOutput.NotOk($"{ex?.Message}"));
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
_logger.LogError(errorInfo);
|
|
||||||
|
|
||||||
// return true to signal that this exception is handled
|
|
||||||
return ValueTask.FromResult(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+3
-17
@@ -13,24 +13,10 @@ namespace IRaCIS.Core.Application.Filter;
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
public class LimitUserRequestAuthorization : IAsyncAuthorizationFilter
|
public class LimitUserRequestAuthorization(
|
||||||
|
IFusionCache _fusionCache, IUserInfo _userInfo, IStringLocalizer _localizer,
|
||||||
|
IOptionsMonitor<ServiceVerifyConfigOption> _verifyConfig) : IAsyncAuthorizationFilter
|
||||||
{
|
{
|
||||||
public IStringLocalizer _localizer { get; set; }
|
|
||||||
|
|
||||||
private readonly IFusionCache _fusionCache;
|
|
||||||
|
|
||||||
private readonly IUserInfo _userInfo;
|
|
||||||
|
|
||||||
private readonly IOptionsMonitor<ServiceVerifyConfigOption> _verifyConfig;
|
|
||||||
|
|
||||||
public LimitUserRequestAuthorization(IFusionCache fusionCache, IUserInfo userInfo, IStringLocalizer localizer, IOptionsMonitor<ServiceVerifyConfigOption> verifyConfig)
|
|
||||||
{
|
|
||||||
_fusionCache = fusionCache;
|
|
||||||
_userInfo = userInfo;
|
|
||||||
_verifyConfig = verifyConfig;
|
|
||||||
_localizer = localizer;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
|
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
|
||||||
{
|
{
|
||||||
+2
-6
@@ -8,13 +8,9 @@ namespace IRaCIS.Core.Application.Filter;
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
public class ModelActionFilter : ActionFilterAttribute, IActionFilter
|
public class ModelActionFilter(IStringLocalizer _localizer) : ActionFilterAttribute, IActionFilter
|
||||||
{
|
{
|
||||||
public IStringLocalizer _localizer;
|
|
||||||
public ModelActionFilter(IStringLocalizer localizer)
|
|
||||||
{
|
|
||||||
_localizer = localizer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnActionExecuting(ActionExecutingContext context)
|
public override void OnActionExecuting(ActionExecutingContext context)
|
||||||
{
|
{
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using IRaCIS.Core.Infrastructure;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.Extensions.Localization;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.Application.Filter;
|
||||||
|
|
||||||
|
public class ProjectExceptionFilter(ILogger<ProjectExceptionFilter> _logger, IStringLocalizer _localizer) : Attribute, IExceptionFilter
|
||||||
|
{
|
||||||
|
|
||||||
|
public void OnException(ExceptionContext context)
|
||||||
|
{
|
||||||
|
//context.ExceptionHandled;//记录当前这个异常是否已经被处理过了
|
||||||
|
|
||||||
|
var exception = context.Exception;
|
||||||
|
|
||||||
|
if (!context.ExceptionHandled)
|
||||||
|
{
|
||||||
|
if (exception.GetType().Name == "DbUpdateConcurrencyException")
|
||||||
|
{
|
||||||
|
//---并发更新,当前不允许该操作
|
||||||
|
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["ProjectException_ConcurrentUpdateNotAllowed"] + exception.Message));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exception.GetType() == typeof(BusinessValidationFailedException))
|
||||||
|
{
|
||||||
|
var error = exception as BusinessValidationFailedException;
|
||||||
|
|
||||||
|
var info = string.Empty;
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(error!.LocalizedKey) && StaticData.Localizer_Dev_Dic.ContainsKey(error!.LocalizedKey))
|
||||||
|
{
|
||||||
|
info = $"[{error!.LocalizedKey}]:{StaticData.Localizer_Dev_Dic[error!.LocalizedKey]}";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
context.Result = new JsonResult(ResponseOutput.NotOk(exception.Message, "", error!.Code, localizedInfo: info));
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (exception.GetType() == typeof(QueryBusinessObjectNotExistException))
|
||||||
|
{
|
||||||
|
context.Result = new JsonResult(ResponseOutput.NotOk(exception.Message, ApiResponseCodeEnum.DataNotExist));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["Project_ExceptionContactDeveloper"] + (exception.InnerException is null ? (exception.Message)
|
||||||
|
: (exception.InnerException?.Message )), ApiResponseCodeEnum.ProgramException));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
context.ExceptionHandled = true;//标记当前异常已经被处理过了
|
||||||
|
|
||||||
|
|
||||||
|
var errorInfo = $"Exception: {exception.Message}[{exception.StackTrace}]" + (exception.InnerException != null ? $" InnerException: {exception.InnerException.Message}[{exception.InnerException.StackTrace}]" : "");
|
||||||
|
|
||||||
|
|
||||||
|
_logger.LogError(errorInfo);
|
||||||
|
|
||||||
|
//_logger.LogError(exception, exception.Message);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//继续
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+189
@@ -0,0 +1,189 @@
|
|||||||
|
using IRaCIS.Core.Application.BusinessFilter;
|
||||||
|
using IRaCIS.Core.Application.Helper;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using static IRaCIS.Core.Domain.Share.StaticData;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.Application.Filter;
|
||||||
|
|
||||||
|
public class TrialGlobalLimitActionFilter(IFusionCache _fusionCache, IUserInfo _userInfo, IRepository<Trial> _trialRepository) : IAsyncActionFilter
|
||||||
|
{
|
||||||
|
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
|
||||||
|
{
|
||||||
|
// 检查当前 endpoint 是否包含 TrialGlobalLimitAttribute 特性
|
||||||
|
var trialLimitAttribute = context.HttpContext.GetEndpoint()?.Metadata.GetMetadata<TrialGlobalLimitAttribute>();
|
||||||
|
|
||||||
|
if (trialLimitAttribute != null)
|
||||||
|
{
|
||||||
|
var optActions = trialLimitAttribute._optActions;
|
||||||
|
|
||||||
|
|
||||||
|
// 这里开始执行原有的逻辑
|
||||||
|
var requestHost = context.HttpContext.Request.Host;
|
||||||
|
|
||||||
|
// 检查请求是否来自 localhost:6100
|
||||||
|
//if (requestHost.Host == "localhost" && (requestHost.Port == 6100 || requestHost.Port == 3305))
|
||||||
|
//{
|
||||||
|
// await next();
|
||||||
|
// return;
|
||||||
|
//}
|
||||||
|
|
||||||
|
#region 特殊用户类型拦截
|
||||||
|
// 用户类型检查
|
||||||
|
if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA && _userInfo.RequestUrl.ToLower() != "TrialDocument/userConfirm".ToLower())
|
||||||
|
{
|
||||||
|
//对不起,您的账户没有操作权限
|
||||||
|
context.Result = new JsonResult(ResponseOutput.NotOk(I18n.T("TrialResource_NoAccessPermission")));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region 从多种途径取TrialId
|
||||||
|
//TrialId 传递的途径多种,可能在path 可能在body 可能在数组中,也可能在对象中,可能就在url
|
||||||
|
var trialIdStr = string.Empty;
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(context.HttpContext.Request.Query["trialId"]))
|
||||||
|
{
|
||||||
|
trialIdStr = context.HttpContext.Request.Query["trialId"];
|
||||||
|
}
|
||||||
|
|
||||||
|
//先尝试从path中取TrialId
|
||||||
|
else if (context.HttpContext.Request.RouteValues.Keys.Any(t => t.Contains("trialId")))
|
||||||
|
{
|
||||||
|
var index = context.HttpContext.Request.RouteValues.Keys.ToList().IndexOf("trialId");
|
||||||
|
trialIdStr = context.HttpContext.Request.RouteValues.Values.ToList()[index] as string;
|
||||||
|
}
|
||||||
|
else if (context.HttpContext.Request.Headers["Referer"].ToString().Contains("trialId"))
|
||||||
|
{
|
||||||
|
var headerStr = context.HttpContext.Request.Headers["Referer"].ToString();
|
||||||
|
|
||||||
|
var trialIdIndex = headerStr.IndexOf("trialId");
|
||||||
|
|
||||||
|
|
||||||
|
var matchResult = Regex.Match(headerStr.Substring(trialIdIndex), @"[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}");
|
||||||
|
|
||||||
|
if (matchResult.Success)
|
||||||
|
{
|
||||||
|
trialIdStr = matchResult.Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//---正则取请求Refer 中trialId 失败,请联系开发人员核查
|
||||||
|
context.Result = new JsonResult(ResponseOutput.NotOk(I18n.T("TrialResource_ReferTrialIdFailed")));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#region body 中取数据
|
||||||
|
|
||||||
|
//设置可以多次读
|
||||||
|
context.HttpContext.Request.EnableBuffering();
|
||||||
|
context.HttpContext.Request.Body.Seek(0, SeekOrigin.Begin);
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
// (?<="trialId" *: *").*?(?=",)
|
||||||
|
|
||||||
|
//使用正则 [0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}
|
||||||
|
|
||||||
|
var matchResult = Regex.Match(contentFromBody.Substring(trialIdIndex), @"[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}");
|
||||||
|
|
||||||
|
if (matchResult.Success)
|
||||||
|
{
|
||||||
|
//有可能匹配错误 "trialId":"","documentId":"b8180000-3e2c-0016-9fe0-08da33f96236" 从缓存里面验证下
|
||||||
|
|
||||||
|
trialIdStr = matchResult.Value;
|
||||||
|
|
||||||
|
var trialStatusStr = await _fusionCache.GetOrSetAsync(CacheKeys.Trial(trialIdStr), _ => CacheHelper.GetTrialStatusAsync(Guid.Parse(trialIdStr), _trialRepository), TimeSpan.FromDays(7));
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(trialStatusStr))
|
||||||
|
{
|
||||||
|
|
||||||
|
//数据库 检查该项目Id不对
|
||||||
|
context.Result = new JsonResult(ResponseOutput.NotOk(I18n.T("TrialResource_ReferTrialIdFailed")));
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//---正则取请求Refer 中trialId 失败,请联系开发人员核查
|
||||||
|
context.Result = new JsonResult(ResponseOutput.NotOk(I18n.T("TrialResource_ReferTrialIdFailed")));
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//使用字符串取 如果是swagger 可能有时取的不对 因为空格的原因
|
||||||
|
//trialIdStr = contentFromBody.Substring(trialIdIndex + "TrialId".Length + 4, 3
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//通过path 或者body 找到trialId 了
|
||||||
|
if (!string.IsNullOrWhiteSpace(trialIdStr))
|
||||||
|
{
|
||||||
|
var trialStatusStr = await _fusionCache.GetOrSetAsync(CacheKeys.Trial(trialIdStr), _ => CacheHelper.GetTrialStatusAsync(Guid.Parse(trialIdStr), _trialRepository), TimeSpan.FromDays(7));
|
||||||
|
|
||||||
|
// 这里是统一拦截 项目有关的操作允许情况(特殊的地方,比如项目配置(有的在多种状态(初始化,ongoing)都可以操作,有的仅仅在Initializing)还有 项目添加和更新,不走这里,特殊处理,不然在这里显得很乱,判断是哪个接口)
|
||||||
|
if (trialStatusStr == StaticData.TrialState.TrialOngoing || optActions.Any(t => t == TrialOpt.BeforeOngoingCantOpt))
|
||||||
|
{
|
||||||
|
|
||||||
|
await next();
|
||||||
|
|
||||||
|
}
|
||||||
|
// 项目停止、或者完成 不允许操作
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//---本次请求被配置规则拦截:项目状态处于进行中时,才允许操作,若此处逻辑有误,请联系开发人员修改
|
||||||
|
context.Result = new JsonResult(ResponseOutput.NotOk(I18n.T("TrialResource_InterceptedProjectStatusRule")));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
//添加项目 签名系统文档的时候 不做拦截 但是更新项目 签名项目文档的时候需要拦截
|
||||||
|
else if (optActions.Any(t => t == TrialOpt.AddOrUpdateTrial || t == TrialOpt.SignSystemDocNoTrialId))
|
||||||
|
{
|
||||||
|
await next();
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//如果项目相关接口没有传递trialId 会来到这里,提醒,以便修改
|
||||||
|
|
||||||
|
//---该接口参数中,没有传递项目编号,请核对。
|
||||||
|
context.Result = new JsonResult(ResponseOutput.NotOk(I18n.T("TrialResource_MissingProjectNumber")));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 如果没有 TrialGlobalLimitAttribute,则直接执行后续逻辑
|
||||||
|
await next();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
+9
-1
@@ -1,10 +1,13 @@
|
|||||||
using IRaCIS.Core.Domain.Share;
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace IRaCIS.Core.Application.Service.BusinessFilter;
|
namespace IRaCIS.Core.Application.Service.BusinessFilter;
|
||||||
|
|
||||||
|
|
||||||
|
#region 控制器流程
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 统一返回前端数据包装,之前在控制器包装,现在修改为动态Api 在ResultFilter这里包装,减少重复冗余代码
|
/// 统一返回前端数据包装,之前在控制器包装,现在修改为动态Api 在ResultFilter这里包装,减少重复冗余代码
|
||||||
/// by zhouhang 2021.09.12 周末
|
/// by zhouhang 2021.09.12 周末
|
||||||
@@ -80,7 +83,7 @@ public class UnifiedApiResultFilter : Attribute, IAsyncResultFilter
|
|||||||
else if (statusCode != 200 && !(objectResult.Value is IResponseOutput))
|
else if (statusCode != 200 && !(objectResult.Value is IResponseOutput))
|
||||||
{
|
{
|
||||||
//---程序错误,请联系开发人员。
|
//---程序错误,请联系开发人员。
|
||||||
var apiResponse = ResponseOutput.NotOk(StaticData.International("UnifiedAPI_ProgramError"));
|
var apiResponse = ResponseOutput.NotOk(I18n.T("UnifiedAPI_ProgramError"));
|
||||||
|
|
||||||
objectResult.Value = apiResponse;
|
objectResult.Value = apiResponse;
|
||||||
objectResult.DeclaredType = apiResponse.GetType();
|
objectResult.DeclaredType = apiResponse.GetType();
|
||||||
@@ -120,3 +123,8 @@ public class UnifiedApiResultFilter : Attribute, IAsyncResultFilter
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
using DocumentFormat.OpenXml.InkML;
|
||||||
|
using IRaCIS.Core.Infrastructure;
|
||||||
|
using Microsoft.AspNetCore.Diagnostics;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.Application.BusinessFilter;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// minimal api 生效,但是传统控制器,没生效
|
||||||
|
/// 参考处理链接: https://learn.microsoft.com/zh-cn/aspnet/core/fundamentals/error-handling?view=aspnetcore-8.0
|
||||||
|
/// </summary>
|
||||||
|
public class GlobalExceptionHandler(IStringLocalizer _localizer, ILogger<GlobalExceptionHandler> _logger) : IExceptionHandler
|
||||||
|
{
|
||||||
|
|
||||||
|
public ValueTask<bool> TryHandleAsync(
|
||||||
|
HttpContext httpContext,
|
||||||
|
Exception exception,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
|
||||||
|
httpContext.Response.StatusCode =StatusCodes.Status200OK;
|
||||||
|
|
||||||
|
httpContext.Response.ContentType = "application/json";
|
||||||
|
|
||||||
|
if (exception.GetType().Name == "DbUpdateConcurrencyException")
|
||||||
|
{
|
||||||
|
//---并发更新,当前不允许该操作
|
||||||
|
|
||||||
|
httpContext.Response.WriteAsJsonAsync(ResponseOutput.NotOk(_localizer["ProjectException_ConcurrentUpdateNotAllowed"] + exception.Message));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exception.GetType() == typeof(BusinessValidationFailedException))
|
||||||
|
{
|
||||||
|
var error = exception as BusinessValidationFailedException;
|
||||||
|
|
||||||
|
var info = string.Empty;
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(error!.LocalizedKey) && StaticData.Localizer_Dev_Dic.ContainsKey(error!.LocalizedKey))
|
||||||
|
{
|
||||||
|
info = $"[{error!.LocalizedKey}]:{StaticData.Localizer_Dev_Dic[error!.LocalizedKey]}";
|
||||||
|
}
|
||||||
|
|
||||||
|
httpContext.Response.WriteAsJsonAsync(ResponseOutput.NotOk(exception.Message, "", error!.Code, localizedInfo: info));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//warning 级别记录
|
||||||
|
//_logger.LogWarning($"[{error!.LocalizedKey}]:{StaticData.Log_Locoalize_Dic[error!.LocalizedKey]}");
|
||||||
|
}
|
||||||
|
else if (exception.GetType() == typeof(QueryBusinessObjectNotExistException))
|
||||||
|
{
|
||||||
|
httpContext.Response.WriteAsJsonAsync(ResponseOutput.NotOk(exception.Message, ApiResponseCodeEnum.DataNotExist));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
httpContext.Response.WriteAsJsonAsync(ResponseOutput.NotOk(_localizer["Project_ExceptionContactDeveloper"] + (exception.InnerException is null ? (exception.Message )
|
||||||
|
: (exception.InnerException?.Message)), ApiResponseCodeEnum.ProgramException));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var errorInfo = $"Exception: {exception.Message}[{exception.StackTrace}]" + (exception.InnerException != null ? $" InnerException: {exception.InnerException.Message}[{exception.InnerException.StackTrace}]" : "");
|
||||||
|
|
||||||
|
_logger.LogError(errorInfo);
|
||||||
|
|
||||||
|
// Return false to continue with the default behavior
|
||||||
|
// - or - return true to signal that this exception is handled
|
||||||
|
return ValueTask.FromResult(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
+85
@@ -0,0 +1,85 @@
|
|||||||
|
using IRaCIS.Core.Application.Helper;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.Application.Service.BusinessFilter;
|
||||||
|
|
||||||
|
#region minimalapi 流程
|
||||||
|
|
||||||
|
|
||||||
|
public class LimitUserRequestAuthorizationEndpointFilter(
|
||||||
|
IFusionCache _fusionCache, IUserInfo _userInfo, IStringLocalizer _localizer,
|
||||||
|
IOptionsMonitor<ServiceVerifyConfigOption> _verifyConfig) : IEndpointFilter
|
||||||
|
{
|
||||||
|
|
||||||
|
public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
|
||||||
|
{
|
||||||
|
var minutes = _verifyConfig.CurrentValue.AutoLoginOutMinutes;
|
||||||
|
|
||||||
|
if (_verifyConfig.CurrentValue.OpenLoginLimit)
|
||||||
|
{
|
||||||
|
// 如果此请求允许匿名访问,不进行处理
|
||||||
|
var endpoint = context.HttpContext.GetEndpoint();
|
||||||
|
if (endpoint?.Metadata?.GetMetadata<IAllowAnonymous>() != null)
|
||||||
|
{
|
||||||
|
return await next(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 没有从请求中取到 token
|
||||||
|
if (string.IsNullOrWhiteSpace(_userInfo.UserToken))
|
||||||
|
{
|
||||||
|
context.HttpContext.Response.ContentType = "application/json";
|
||||||
|
return Results.Json(ResponseOutput.NotOk(_localizer["LimitUser_AuthTokenMissing"]), statusCode: StatusCodes.Status200OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取缓存中的用户 token
|
||||||
|
var cacheUserToken = await _fusionCache.GetOrDefaultAsync<string>(CacheKeys.UserToken(_userInfo.Id));
|
||||||
|
|
||||||
|
// 缓存中没有取到 token
|
||||||
|
if (string.IsNullOrWhiteSpace(cacheUserToken))
|
||||||
|
{
|
||||||
|
// 设置当前用户最新 token
|
||||||
|
await _fusionCache.SetAsync(CacheKeys.UserToken(_userInfo.Id), _userInfo.UserToken, TimeSpan.FromDays(7));
|
||||||
|
await _fusionCache.SetAsync(CacheKeys.UserAutoLoginOut(_userInfo.Id), DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TimeSpan.FromMinutes(minutes));
|
||||||
|
}
|
||||||
|
// 如果是同一个用户
|
||||||
|
else if (cacheUserToken == _userInfo.UserToken)
|
||||||
|
{
|
||||||
|
var cacheTime = await _fusionCache.GetOrDefaultAsync<string>(CacheKeys.UserAutoLoginOut(_userInfo.Id));
|
||||||
|
|
||||||
|
// 如果过期,自动登出
|
||||||
|
if (string.IsNullOrEmpty(cacheTime))
|
||||||
|
{
|
||||||
|
context.HttpContext.Response.ContentType = "application/json";
|
||||||
|
return Results.Json(ResponseOutput.NotOk(_localizer["LimitUser_AccountAuto_LoginOut"], ApiResponseCodeEnum.AutoLoginOut), statusCode: StatusCodes.Status403Forbidden);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await _fusionCache.SetAsync(CacheKeys.UserAutoLoginOut(_userInfo.Id), DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TimeSpan.FromMinutes(minutes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 如果账户在其他地方已登录,当前用户被迫下线
|
||||||
|
context.HttpContext.Response.ContentType = "application/json";
|
||||||
|
return Results.Json(ResponseOutput.NotOk(_localizer["LimitUser_AccountLoggedInElsewhere"], ApiResponseCodeEnum.LoginInOtherPlace), statusCode: StatusCodes.Status403Forbidden);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果通过授权,继续执行请求管道
|
||||||
|
return await next(context);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.Application.BusinessFilter;
|
||||||
|
|
||||||
|
public class ModelValidationEndpointFilter(IStringLocalizer _localizer) : IEndpointFilter
|
||||||
|
{
|
||||||
|
public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
|
||||||
|
{
|
||||||
|
var httpContext = context.HttpContext;
|
||||||
|
|
||||||
|
//// 1. 获取路由参数并验证
|
||||||
|
//foreach (var routeValue in httpContext.Request.RouteValues)
|
||||||
|
//{
|
||||||
|
// {
|
||||||
|
// return Results.BadRequest(new { ErrorMessage = $"Invalid format for {routeValue.Key}." });
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
//// 2. 获取查询参数并验证
|
||||||
|
//foreach (var queryValue in httpContext.Request.Query)
|
||||||
|
//{
|
||||||
|
// {
|
||||||
|
// return Results.BadRequest(new { ErrorMessage = $"Invalid format for {queryValue.Key}." });
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
// 获取请求中的模型参数
|
||||||
|
var model = context.Arguments.FirstOrDefault(a => a != null && a.GetType().IsClass);
|
||||||
|
|
||||||
|
// 仅当模型存在时执行验证
|
||||||
|
if (model != null)
|
||||||
|
{
|
||||||
|
var validationResults = new List<ValidationResult>();
|
||||||
|
var validationContext = new ValidationContext(model);
|
||||||
|
bool isValid = Validator.TryValidateObject(model, validationContext, validationResults, true);
|
||||||
|
|
||||||
|
if (!isValid)
|
||||||
|
{
|
||||||
|
var validationErrors = validationResults.Select(vr => vr.ErrorMessage).ToArray();
|
||||||
|
return Results.Json(new
|
||||||
|
{
|
||||||
|
ErrorMessage = _localizer["ModelAction_InvalidAPIParameter"],
|
||||||
|
Errors = validationErrors
|
||||||
|
}, statusCode: StatusCodes.Status400BadRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证通过,继续执行
|
||||||
|
return await next(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,191 @@
|
|||||||
|
using IRaCIS.Core.Application.Helper;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using static IRaCIS.Core.Domain.Share.StaticData;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.Application.BusinessFilter;
|
||||||
|
|
||||||
|
public class TrialGlobalLimitAttribute : Attribute
|
||||||
|
{
|
||||||
|
public readonly string[] _optActions;
|
||||||
|
|
||||||
|
public TrialGlobalLimitAttribute(params string[] optActions)
|
||||||
|
{
|
||||||
|
_optActions = optActions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public class TrialGlobalLimitEndpointFilter(IFusionCache _fusionCache, IUserInfo _userInfo, IRepository<Trial> _trialRepository) : IEndpointFilter
|
||||||
|
|
||||||
|
{
|
||||||
|
public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
|
||||||
|
{
|
||||||
|
// 检查当前 endpoint 是否包含 TrialGlobalLimitAttribute 特性
|
||||||
|
var trialLimitAttribute = context.HttpContext.GetEndpoint()?.Metadata.GetMetadata<TrialGlobalLimitAttribute>();
|
||||||
|
|
||||||
|
if (trialLimitAttribute != null)
|
||||||
|
{
|
||||||
|
var optActions = trialLimitAttribute._optActions;
|
||||||
|
|
||||||
|
|
||||||
|
// 这里开始执行原有的逻辑
|
||||||
|
var requestHost = context.HttpContext.Request.Host;
|
||||||
|
|
||||||
|
// 检查请求是否来自 localhost:6100
|
||||||
|
if (requestHost.Host == "localhost" && (requestHost.Port == 6100 || requestHost.Port == 3305))
|
||||||
|
{
|
||||||
|
return await next(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region 特殊用户类型拦截
|
||||||
|
// 用户类型检查
|
||||||
|
if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA && _userInfo.RequestUrl.ToLower() != "TrialDocument/userConfirm".ToLower())
|
||||||
|
{
|
||||||
|
//对不起,您的账户没有操作权限
|
||||||
|
return Results.Json(ResponseOutput.NotOk(I18n.T("TrialResource_NoAccessPermission")));
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region 从多种途径取TrialId
|
||||||
|
//TrialId 传递的途径多种,可能在path 可能在body 可能在数组中,也可能在对象中,可能就在url
|
||||||
|
var trialIdStr = string.Empty;
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(context.HttpContext.Request.Query["trialId"]))
|
||||||
|
{
|
||||||
|
trialIdStr = context.HttpContext.Request.Query["trialId"];
|
||||||
|
}
|
||||||
|
|
||||||
|
//先尝试从path中取TrialId
|
||||||
|
else if (context.HttpContext.Request.RouteValues.Keys.Any(t => t.Contains("trialId")))
|
||||||
|
{
|
||||||
|
var index = context.HttpContext.Request.RouteValues.Keys.ToList().IndexOf("trialId");
|
||||||
|
trialIdStr = context.HttpContext.Request.RouteValues.Values.ToList()[index] as string;
|
||||||
|
}
|
||||||
|
else if (context.HttpContext.Request.Headers["Referer"].ToString().Contains("trialId"))
|
||||||
|
{
|
||||||
|
var headerStr = context.HttpContext.Request.Headers["Referer"].ToString();
|
||||||
|
|
||||||
|
var trialIdIndex = headerStr.IndexOf("trialId");
|
||||||
|
|
||||||
|
|
||||||
|
var matchResult = Regex.Match(headerStr.Substring(trialIdIndex), @"[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}");
|
||||||
|
|
||||||
|
if (matchResult.Success)
|
||||||
|
{
|
||||||
|
trialIdStr = matchResult.Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//---正则取请求Refer 中trialId 失败,请联系开发人员核查
|
||||||
|
return Results.Json(ResponseOutput.NotOk(I18n.T("TrialResource_ReferTrialIdFailed")));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#region body 中取数据
|
||||||
|
|
||||||
|
//设置可以多次读
|
||||||
|
context.HttpContext.Request.EnableBuffering();
|
||||||
|
context.HttpContext.Request.Body.Seek(0, SeekOrigin.Begin);
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
// (?<="trialId" *: *").*?(?=",)
|
||||||
|
|
||||||
|
//使用正则 [0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}
|
||||||
|
|
||||||
|
var matchResult = Regex.Match(contentFromBody.Substring(trialIdIndex), @"[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}");
|
||||||
|
|
||||||
|
if (matchResult.Success)
|
||||||
|
{
|
||||||
|
//有可能匹配错误 "trialId":"","documentId":"b8180000-3e2c-0016-9fe0-08da33f96236" 从缓存里面验证下
|
||||||
|
|
||||||
|
trialIdStr = matchResult.Value;
|
||||||
|
|
||||||
|
var trialStatusStr = await _fusionCache.GetOrSetAsync(CacheKeys.Trial(trialIdStr), _ => CacheHelper.GetTrialStatusAsync(Guid.Parse(trialIdStr), _trialRepository), TimeSpan.FromDays(7));
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(trialStatusStr))
|
||||||
|
{
|
||||||
|
|
||||||
|
//数据库 检查该项目Id不对
|
||||||
|
return Results.Json(ResponseOutput.NotOk(I18n.T("TrialResource_ReferTrialIdFailed")));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//---正则取请求Refer 中trialId 失败,请联系开发人员核查
|
||||||
|
return Results.Json(ResponseOutput.NotOk(I18n.T("TrialResource_ReferTrialIdFailed")));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//使用字符串取 如果是swagger 可能有时取的不对 因为空格的原因
|
||||||
|
//trialIdStr = contentFromBody.Substring(trialIdIndex + "TrialId".Length + 4, 3
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//通过path 或者body 找到trialId 了
|
||||||
|
if (!string.IsNullOrWhiteSpace(trialIdStr))
|
||||||
|
{
|
||||||
|
var trialStatusStr = await _fusionCache.GetOrSetAsync(CacheKeys.Trial(trialIdStr), _ => CacheHelper.GetTrialStatusAsync(Guid.Parse(trialIdStr), _trialRepository), TimeSpan.FromDays(7));
|
||||||
|
|
||||||
|
// 这里是统一拦截 项目有关的操作允许情况(特殊的地方,比如项目配置(有的在多种状态(初始化,ongoing)都可以操作,有的仅仅在Initializing)还有 项目添加和更新,不走这里,特殊处理,不然在这里显得很乱,判断是哪个接口)
|
||||||
|
if (trialStatusStr == StaticData.TrialState.TrialOngoing || optActions.Any(t => t == TrialOpt.BeforeOngoingCantOpt))
|
||||||
|
{
|
||||||
|
|
||||||
|
return await next(context);
|
||||||
|
|
||||||
|
}
|
||||||
|
// 项目停止、或者完成 不允许操作
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//---本次请求被配置规则拦截:项目状态处于进行中时,才允许操作,若此处逻辑有误,请联系开发人员修改
|
||||||
|
return Results.Json(ResponseOutput.NotOk(I18n.T("TrialResource_InterceptedProjectStatusRule")));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
//添加项目 签名系统文档的时候 不做拦截 但是更新项目 签名项目文档的时候需要拦截
|
||||||
|
else if (optActions.Any(t => t == TrialOpt.AddOrUpdateTrial || t == TrialOpt.SignSystemDocNoTrialId))
|
||||||
|
{
|
||||||
|
return await next(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//如果项目相关接口没有传递trialId 会来到这里,提醒,以便修改
|
||||||
|
|
||||||
|
//---该接口参数中,没有传递项目编号,请核对。
|
||||||
|
return Results.Json(ResponseOutput.NotOk(I18n.T("TrialResource_MissingProjectNumber")));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有 TrialGlobalLimitAttribute,则直接执行后续逻辑
|
||||||
|
return await next(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.Application.Service.BusinessFilter;
|
||||||
|
|
||||||
|
#region minimalapi 流程
|
||||||
|
|
||||||
|
|
||||||
|
public class UnifiedApiResultEndpointFilter(ILogger<UnifiedApiResultEndpointFilter> _logger) : IEndpointFilter
|
||||||
|
{
|
||||||
|
|
||||||
|
public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
|
||||||
|
{
|
||||||
|
// 调用下一个过滤器或端点处理程序
|
||||||
|
var result = await next(context);
|
||||||
|
|
||||||
|
// 检查返回值类型
|
||||||
|
if (result is null)
|
||||||
|
{
|
||||||
|
return ResponseOutput.NotOk("No data found."); // 处理 null 返回
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果返回的是元组
|
||||||
|
if (result is ValueTuple<object, object> tuple)
|
||||||
|
{
|
||||||
|
return ResponseOutput.Ok(tuple.Item1, tuple.Item2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result is IResponseOutput responseOutput)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(responseOutput.LocalizedInfo))
|
||||||
|
{
|
||||||
|
//统一在这里记录国际化的日志信息
|
||||||
|
_logger.LogWarning($"{responseOutput.LocalizedInfo}");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对于其他情况,直接返回结果
|
||||||
|
return ResponseOutput.Ok(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IResponseOutput WrapResponse(object? value)
|
||||||
|
{
|
||||||
|
// 处理元组的情况
|
||||||
|
if (value is ValueTuple<object, object> tuple)
|
||||||
|
{
|
||||||
|
return ResponseOutput.Ok(tuple.Item1, tuple.Item2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 包装单个对象
|
||||||
|
return ResponseOutput.Ok(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
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
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
using IRaCIS.Core.Domain.Share;
|
|
||||||
using IRaCIS.Core.Infrastructure;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
|
||||||
using Microsoft.Extensions.Localization;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.Application.Filter;
|
|
||||||
|
|
||||||
public class ProjectExceptionFilter : Attribute, IExceptionFilter
|
|
||||||
{
|
|
||||||
private readonly ILogger<ProjectExceptionFilter> _logger;
|
|
||||||
|
|
||||||
public IStringLocalizer _localizer;
|
|
||||||
|
|
||||||
public ProjectExceptionFilter(IStringLocalizer localizer, ILogger<ProjectExceptionFilter> logger)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
_localizer = localizer;
|
|
||||||
}
|
|
||||||
public void OnException(ExceptionContext context)
|
|
||||||
{
|
|
||||||
//context.ExceptionHandled;//记录当前这个异常是否已经被处理过了
|
|
||||||
|
|
||||||
if (!context.ExceptionHandled)
|
|
||||||
{
|
|
||||||
if (context.Exception.GetType().Name == "DbUpdateConcurrencyException")
|
|
||||||
{
|
|
||||||
//---并发更新,当前不允许该操作
|
|
||||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["ProjectException_ConcurrentUpdateNotAllowed"] + context.Exception.Message));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context.Exception.GetType() == typeof(BusinessValidationFailedException))
|
|
||||||
{
|
|
||||||
var error = context.Exception as BusinessValidationFailedException;
|
|
||||||
|
|
||||||
var info = string.Empty;
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(error!.LocalizedKey) && StaticData.Log_Locoalize_Dic.ContainsKey(error!.LocalizedKey))
|
|
||||||
{
|
|
||||||
info = $"[{error!.LocalizedKey}]:{StaticData.Log_Locoalize_Dic[error!.LocalizedKey]}";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
context.Result = new JsonResult(ResponseOutput.NotOk(context.Exception.Message, "", error!.Code, localizedInfo: info));
|
|
||||||
|
|
||||||
|
|
||||||
//warning 级别记录
|
|
||||||
//_logger.LogWarning($"[{error!.LocalizedKey}]:{StaticData.Log_Locoalize_Dic[error!.LocalizedKey]}");
|
|
||||||
}
|
|
||||||
else if (context.Exception.GetType() == typeof(QueryBusinessObjectNotExistException))
|
|
||||||
{
|
|
||||||
context.Result = new JsonResult(ResponseOutput.NotOk(context.Exception.Message, ApiResponseCodeEnum.DataNotExist));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["Project_ExceptionContactDeveloper"] + (context.Exception.InnerException is null ? (context.Exception.Message /*+ context.Exception.StackTrace*/)
|
|
||||||
: (context.Exception.InnerException?.Message /*+ context.Exception.InnerException?.StackTrace*/)), ApiResponseCodeEnum.ProgramException));
|
|
||||||
|
|
||||||
_logger.LogError(context.Exception.InnerException is null ? (context.Exception.Message + context.Exception.StackTrace) : (context.Exception.InnerException?.Message + context.Exception.InnerException?.StackTrace));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
context.ExceptionHandled = true;//标记当前异常已经被处理过了
|
|
||||||
|
|
||||||
|
|
||||||
//throw new Exception("test-result-exceptioin");
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//继续
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -34,6 +34,8 @@ public class ServiceVerifyConfigOption
|
|||||||
[Description("修改密码的天数")]
|
[Description("修改密码的天数")]
|
||||||
public int ChangePassWordDays { get; set; }
|
public int ChangePassWordDays { get; set; }
|
||||||
|
|
||||||
|
public string ThirdPdfUrl { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SystemEmailSendConfig
|
public class SystemEmailSendConfig
|
||||||
@@ -61,6 +63,10 @@ public class SystemEmailSendConfig
|
|||||||
public string CompanyShortNameCN { get; set; } = string.Empty;
|
public string CompanyShortNameCN { get; set; } = string.Empty;
|
||||||
|
|
||||||
public bool IsEnv_US { get; set; }
|
public bool IsEnv_US { get; set; }
|
||||||
|
|
||||||
|
public bool IsOpenErrorNoticeEmail { get; set; }
|
||||||
|
|
||||||
|
public List<string> ErrorNoticeEmailList { get; set; } =new List<string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SystemEmailSendConfigView
|
public class SystemEmailSendConfigView
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ global using IRaCIS.Core.Infra.EFCore;
|
|||||||
global using IRaCIS.Core.Infrastructure.Extention;
|
global using IRaCIS.Core.Infrastructure.Extention;
|
||||||
global using Microsoft.EntityFrameworkCore;
|
global using Microsoft.EntityFrameworkCore;
|
||||||
global using System.Linq.Expressions;
|
global using System.Linq.Expressions;
|
||||||
|
global using ZiggyCreatures.Caching.Fusion;
|
||||||
|
global using Microsoft.Extensions.Localization;
|
||||||
|
global using AutoMapper;
|
||||||
|
global using IRaCIS.Core.Domain.Share;
|
||||||
|
global using IRaCIS.Core.Application.BusinessFilter;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -42,7 +42,7 @@ public static class SendEmailHelper
|
|||||||
{
|
{
|
||||||
|
|
||||||
//---邮件发送失败,您进行的操作未能成功,请检查邮箱或联系维护人员
|
//---邮件发送失败,您进行的操作未能成功,请检查邮箱或联系维护人员
|
||||||
throw new Exception(StaticData.International("SendEmail_SendFail"), new Exception(ex.Message));
|
throw new Exception(I18n.T("SendEmail_SendFail"), new Exception(ex.Message));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ public static class SendEmailHelper
|
|||||||
if (sMTPEmailConfig.ToMailAddressList.Count == 0)
|
if (sMTPEmailConfig.ToMailAddressList.Count == 0)
|
||||||
{
|
{
|
||||||
//---没有收件人
|
//---没有收件人
|
||||||
throw new ArgumentException(StaticData.International("SendEmail_NoRecipient"));
|
throw new ArgumentException(I18n.T("SendEmail_NoRecipient"));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.Application.Helper;
|
|
||||||
|
|
||||||
public class FileConvertHelper
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
static public void ConvertWordToPdf(string inputWordFilePath, string outputPdfDir)
|
|
||||||
{
|
|
||||||
// 设置 libreoffice 命令行参数
|
|
||||||
string arguments = $"--headless --invisible --convert-to pdf \"{inputWordFilePath}\" --outdir \"{outputPdfDir}\"";
|
|
||||||
|
|
||||||
// 启动 libreoffice 进程
|
|
||||||
using (Process process = new Process())
|
|
||||||
{
|
|
||||||
process.StartInfo.FileName = "libreoffice";
|
|
||||||
process.StartInfo.Arguments = arguments;
|
|
||||||
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
|
|
||||||
process.StartInfo.RedirectStandardOutput = true;
|
|
||||||
process.StartInfo.RedirectStandardError = true;
|
|
||||||
process.StartInfo.UseShellExecute = false;
|
|
||||||
process.StartInfo.CreateNoWindow = true;
|
|
||||||
|
|
||||||
process.Start();
|
|
||||||
|
|
||||||
// 等待进程结束
|
|
||||||
process.WaitForExit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
+200
-7
@@ -1,4 +1,6 @@
|
|||||||
using IRaCIS.Application.Contracts;
|
using DocumentFormat.OpenXml.Spreadsheet;
|
||||||
|
using DocumentFormat.OpenXml.Wordprocessing;
|
||||||
|
using IRaCIS.Application.Contracts;
|
||||||
using IRaCIS.Application.Interfaces;
|
using IRaCIS.Application.Interfaces;
|
||||||
using IRaCIS.Core.API._ServiceExtensions.NewtonsoftJson;
|
using IRaCIS.Core.API._ServiceExtensions.NewtonsoftJson;
|
||||||
using IRaCIS.Core.Application.Helper;
|
using IRaCIS.Core.Application.Helper;
|
||||||
@@ -195,15 +197,64 @@ public static class ExcelExportHelper
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class DynamicColumnConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 增加动态列开始索引 从0 开始算
|
||||||
|
/// </summary>
|
||||||
|
public int AutoColumnStartIndex { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 动态列开始的行index 从0开始
|
||||||
|
/// </summary>
|
||||||
|
public int AutoColumnTitleRowIndex { get; set; }
|
||||||
|
|
||||||
|
|
||||||
public static async Task<(MemoryStream, string)> DataExport_NpoiTestAsync(string code, ExcelExportInfo data, IRepository<CommonDocument> _commonDocumentRepository, IWebHostEnvironment _hostEnvironment, IDictionaryService? _dictionaryService = null, Type? translateType = null, CriterionType? criterionType = null)
|
public int TempalteLastColumnIndex { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 动态的列名
|
||||||
|
/// </summary>
|
||||||
|
public List<string> ColumnNameList { get; set; } = new List<string>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 动态翻译的字典名
|
||||||
|
/// </summary>
|
||||||
|
public List<string> TranslateDicNameList { get; set; } = new List<string>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 动态取数据的集合名
|
||||||
|
/// </summary>
|
||||||
|
public string DynamicListName { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 动态数据翻译的取字典的属性名
|
||||||
|
/// </summary>
|
||||||
|
public string DynamicItemDicName { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 取值的属性名
|
||||||
|
/// </summary>
|
||||||
|
public string DynamicItemValueName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Excel Title Name
|
||||||
|
/// </summary>
|
||||||
|
public string DynamicItemTitleName { get; set; }
|
||||||
|
|
||||||
|
public List<int> RemoveColunmIndexList { get; set; } = new List<int>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static async Task<(MemoryStream, string)> DataExport_NpoiTestAsync(string code, ExcelExportInfo data, IRepository<CommonDocument> _commonDocumentRepository, IWebHostEnvironment _hostEnvironment, IDictionaryService? _dictionaryService = null, Type? translateType = null, CriterionType? criterionType = null, DynamicColumnConfig? dynamicColumnConfig = null)
|
||||||
{
|
{
|
||||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
||||||
//判断是否有字典翻译
|
//判断是否有字典翻译
|
||||||
|
|
||||||
object translateData = data;
|
object translateData = data;
|
||||||
|
|
||||||
|
Dictionary<string, object> translatedDic = default;
|
||||||
|
|
||||||
if (_dictionaryService != null && translateType != null)
|
if (_dictionaryService != null && translateType != null)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -284,6 +335,7 @@ public static class ExcelExportHelper
|
|||||||
|
|
||||||
//data = dic;
|
//data = dic;
|
||||||
translateData = dic;
|
translateData = dic;
|
||||||
|
translatedDic = dic;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,7 +371,91 @@ public static class ExcelExportHelper
|
|||||||
workbook.RemoveSheetAt(1);
|
workbook.RemoveSheetAt(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
var memoryStream2 = new MemoryStream();
|
|
||||||
|
if (dynamicColumnConfig != null)
|
||||||
|
{
|
||||||
|
var sheet = workbook.GetSheetAt(0);
|
||||||
|
|
||||||
|
var titelRow = sheet.GetRow(dynamicColumnConfig.AutoColumnTitleRowIndex);
|
||||||
|
var templateRow = sheet.GetRow(dynamicColumnConfig.AutoColumnTitleRowIndex + 1);
|
||||||
|
|
||||||
|
//动态移除列的数量
|
||||||
|
var dynamicRemoveColunmCount = dynamicColumnConfig.RemoveColunmIndexList.Count();
|
||||||
|
|
||||||
|
var beforeDynamicRemoveCount = dynamicColumnConfig.RemoveColunmIndexList.Where(t => t < dynamicColumnConfig.AutoColumnStartIndex).Count();
|
||||||
|
|
||||||
|
//动态添加列的数量
|
||||||
|
var needAddCount = dynamicColumnConfig.ColumnNameList.Count;
|
||||||
|
|
||||||
|
//原始表 最终索引
|
||||||
|
var originTotalEndIndex = dynamicColumnConfig.TempalteLastColumnIndex;
|
||||||
|
|
||||||
|
//减去动态移除后原始结束索引
|
||||||
|
var originRemoveEndIndex= originTotalEndIndex- dynamicRemoveColunmCount;
|
||||||
|
|
||||||
|
//最终表 动态列开始索引
|
||||||
|
var dynamicColunmStartIndex = dynamicColumnConfig.AutoColumnStartIndex - beforeDynamicRemoveCount;
|
||||||
|
|
||||||
|
//最终表 动态列的终止索引
|
||||||
|
var dynamicColunmEndIndex = dynamicColunmStartIndex + needAddCount - 1;
|
||||||
|
|
||||||
|
//最终表 最终索引
|
||||||
|
var totalColunmEndIndex = originTotalEndIndex + needAddCount - dynamicRemoveColunmCount;
|
||||||
|
|
||||||
|
|
||||||
|
//动态列后需要移动的数量
|
||||||
|
var backMoveCount = totalColunmEndIndex - dynamicColunmEndIndex;
|
||||||
|
|
||||||
|
//删除需要动态删除的列 从大到小移除,否则索引会变
|
||||||
|
foreach (var removeIndex in dynamicColumnConfig.RemoveColunmIndexList.OrderByDescending(t => t))
|
||||||
|
{
|
||||||
|
//将后面的列向前移动
|
||||||
|
for (var i = 0; i < originTotalEndIndex - removeIndex; i++)
|
||||||
|
{
|
||||||
|
Console.WriteLine(titelRow.GetCell(removeIndex + i + 1).StringCellValue);
|
||||||
|
titelRow.GetCell(removeIndex + i).SetCellValue(titelRow.GetCell(removeIndex + i + 1).StringCellValue);
|
||||||
|
templateRow.GetCell(removeIndex + i).SetCellValue(templateRow.GetCell(removeIndex + i + 1).StringCellValue);
|
||||||
|
|
||||||
|
//后面的数据要清空
|
||||||
|
titelRow.GetCell(removeIndex + i + 1).SetCellValue("");
|
||||||
|
templateRow.GetCell(removeIndex + i + 1).SetCellValue("");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//创建新的列
|
||||||
|
for (int i = originTotalEndIndex; i < originTotalEndIndex + needAddCount; i++)
|
||||||
|
{
|
||||||
|
titelRow.CreateCell(i + 1);
|
||||||
|
templateRow.CreateCell(i + 1);
|
||||||
|
}
|
||||||
|
//移动Title 和下面的模板标识
|
||||||
|
|
||||||
|
var gap = totalColunmEndIndex - originRemoveEndIndex;
|
||||||
|
|
||||||
|
for (int i = totalColunmEndIndex; i > dynamicColunmEndIndex; i--)
|
||||||
|
{
|
||||||
|
|
||||||
|
titelRow.GetCell(i).SetCellValue(titelRow.GetCell(i - gap).StringCellValue);
|
||||||
|
|
||||||
|
templateRow.GetCell(i).SetCellValue(templateRow.GetCell(i - gap).StringCellValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//设置动态Tilte
|
||||||
|
|
||||||
|
for (int i = dynamicColunmStartIndex; i < dynamicColunmStartIndex + needAddCount; i++)
|
||||||
|
{
|
||||||
|
var name = dynamicColumnConfig.ColumnNameList[i - dynamicColunmStartIndex];
|
||||||
|
|
||||||
|
titelRow.GetCell(i).SetCellValue(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var memoryStream2 = new MemoryStream())
|
||||||
|
{
|
||||||
workbook.Write(memoryStream2, true);
|
workbook.Write(memoryStream2, true);
|
||||||
|
|
||||||
memoryStream2.Seek(0, SeekOrigin.Begin);
|
memoryStream2.Seek(0, SeekOrigin.Begin);
|
||||||
@@ -327,8 +463,9 @@ public static class ExcelExportHelper
|
|||||||
templateStream = memoryStream2;
|
templateStream = memoryStream2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 文件名称 从sheet里面取
|
|
||||||
//fileName = workbook.GetSheetName(0);
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region MiniExcel
|
#region MiniExcel
|
||||||
@@ -344,12 +481,68 @@ public static class ExcelExportHelper
|
|||||||
|
|
||||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
if (dynamicColumnConfig != null)
|
||||||
|
{
|
||||||
|
var dynamicTranslateDataList = await _dictionaryService.GetBasicDataSelect(dynamicColumnConfig.TranslateDicNameList.ToArray());
|
||||||
|
|
||||||
|
// 使用NPOI 进行二次处理
|
||||||
|
var wb = new XSSFWorkbook(memoryStream);
|
||||||
|
var sheet = wb.GetSheetAt(0);
|
||||||
|
|
||||||
|
var list = translatedDic["List"] as IList;
|
||||||
|
|
||||||
|
foreach (var itemResult in list)
|
||||||
|
{
|
||||||
|
var index = list.IndexOf(itemResult);
|
||||||
|
|
||||||
|
//从第四行开始处理动态列
|
||||||
|
var row = sheet.GetRow(index + dynamicColumnConfig.AutoColumnTitleRowIndex + 1);
|
||||||
|
|
||||||
|
var itemDic = itemResult.ToDictionary();
|
||||||
|
|
||||||
|
var itemList = itemDic[dynamicColumnConfig.DynamicListName] as IList;
|
||||||
|
|
||||||
|
//这个数组是动态的,有的多,有的少,所以在此对比Title 一致才赋值
|
||||||
|
foreach (var itemObj in itemList)
|
||||||
|
{
|
||||||
|
|
||||||
|
var iteObjDic = itemObj.ToDictionary();
|
||||||
|
|
||||||
|
var itemDicName = iteObjDic[dynamicColumnConfig.DynamicItemDicName]?.ToString();
|
||||||
|
var itemValue = iteObjDic[dynamicColumnConfig.DynamicItemValueName]?.ToString();
|
||||||
|
|
||||||
|
//var writeIndex = itemList.IndexOf(itemObj) + dynamicColumnConfig.AutoColumnStartIndex;
|
||||||
|
|
||||||
|
var writeIndex = dynamicColumnConfig.ColumnNameList.IndexOf(iteObjDic[dynamicColumnConfig.DynamicItemTitleName].ToString()) + dynamicColumnConfig.AutoColumnStartIndex;
|
||||||
|
|
||||||
|
if (itemDicName.IsNotNullOrEmpty())
|
||||||
|
{
|
||||||
|
|
||||||
|
var translatedItemData = dynamicTranslateDataList[itemDicName].Where(t => t.Code.ToLower() == itemValue?.ToLower()).Select(t => isEn_US ? t.Value : t.ValueCN).FirstOrDefault() ?? String.Empty;
|
||||||
|
|
||||||
|
row.GetCell(writeIndex).SetCellValue(translatedItemData);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
row.GetCell(writeIndex).SetCellValue(itemValue);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var memoryStream2 = new MemoryStream();
|
||||||
|
wb.Write(memoryStream2, true);
|
||||||
|
memoryStream2.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
memoryStream = memoryStream2;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return (memoryStream, fileName);
|
return (memoryStream, fileName);
|
||||||
|
|
||||||
//var wb = new HSSFWorkbook(memoryStream);
|
|
||||||
|
|
||||||
//var sheet = wb.GetSheetAt(0);
|
|
||||||
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using RestSharp;
|
||||||
|
using SharpCompress.Common;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.Application.Helper;
|
||||||
|
|
||||||
|
public class FileConvertHelper
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 镜像里面打入libreoffice 的方案
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="inputWordFilePath"></param>
|
||||||
|
/// <param name="outputPdfDir"></param>
|
||||||
|
static public void ConvertWordToPdf(string inputWordFilePath, string outputPdfDir)
|
||||||
|
{
|
||||||
|
// 设置 libreoffice 命令行参数
|
||||||
|
string arguments = $"--headless --invisible --convert-to pdf \"{inputWordFilePath}\" --outdir \"{outputPdfDir}\"";
|
||||||
|
|
||||||
|
// 启动 libreoffice 进程
|
||||||
|
using (Process process = new Process())
|
||||||
|
{
|
||||||
|
process.StartInfo.FileName = "libreoffice";
|
||||||
|
process.StartInfo.Arguments = arguments;
|
||||||
|
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
|
||||||
|
process.StartInfo.RedirectStandardOutput = true;
|
||||||
|
process.StartInfo.RedirectStandardError = true;
|
||||||
|
process.StartInfo.UseShellExecute = false;
|
||||||
|
process.StartInfo.CreateNoWindow = true;
|
||||||
|
|
||||||
|
process.Start();
|
||||||
|
|
||||||
|
// 等待进程结束
|
||||||
|
process.WaitForExit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 返回文件字节数组
|
||||||
|
/// File.WriteAllBytes(outputFilePath, response.RawBytes); 直接将字节数组写入到本地某个路径
|
||||||
|
/// var stream = new MemoryStream(response.RawBytes); 直接变为内存流,给其他程序用也可以
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
static public async Task<byte[]> ThirdStirling_PDFWordToPdfAsync(string filePath)
|
||||||
|
{
|
||||||
|
|
||||||
|
var apiUrl = GetApiUrl();
|
||||||
|
|
||||||
|
var client = new RestClient(apiUrl);
|
||||||
|
|
||||||
|
var request = new RestRequest(apiUrl, Method.Post);
|
||||||
|
|
||||||
|
request.AddHeader("accept", "*/*");
|
||||||
|
|
||||||
|
// 直接上传文件,无需生成字节数组
|
||||||
|
request.AddFile("fileInput", filePath);
|
||||||
|
|
||||||
|
return await GetPDFByteArrayAsync(client, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
static public async Task<byte[]> ThirdStirling_PDFWordToPdfAsync(byte[] fileBytes, string fileName)
|
||||||
|
{
|
||||||
|
|
||||||
|
var apiUrl = GetApiUrl();
|
||||||
|
var client = new RestClient(apiUrl);
|
||||||
|
|
||||||
|
var request = new RestRequest(apiUrl, Method.Post);
|
||||||
|
|
||||||
|
request.AddHeader("accept", "*/*");
|
||||||
|
|
||||||
|
// 添加文件流到请求
|
||||||
|
request.AddFile("fileInput", fileBytes, fileName);
|
||||||
|
|
||||||
|
return await GetPDFByteArrayAsync(client, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
static private async Task<byte[]> GetPDFByteArrayAsync(RestClient client, RestRequest request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 发送请求并获取响应
|
||||||
|
var response = await client.ExecuteAsync(request);
|
||||||
|
|
||||||
|
// 检查请求是否成功
|
||||||
|
if (response.IsSuccessful)
|
||||||
|
{
|
||||||
|
return response.RawBytes ?? new byte[] { };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine($"上传失败,错误代码: {response.StatusCode}, 错误消息: {response.ErrorMessage}");
|
||||||
|
|
||||||
|
return Array.Empty<byte>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine("发生异常: " + ex.Message);
|
||||||
|
|
||||||
|
return Array.Empty<byte>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static private string GetApiUrl()
|
||||||
|
{
|
||||||
|
|
||||||
|
var enviromentName = Environment.GetEnvironmentVariables()["ASPNETCORE_ENVIRONMENT"]?.ToString();
|
||||||
|
|
||||||
|
var configuration = new ConfigurationBuilder().AddJsonFile($"appsettings.{enviromentName}.json", false, false).Build();
|
||||||
|
|
||||||
|
// 手动绑定配置
|
||||||
|
var appSettings = new ServiceVerifyConfigOption();
|
||||||
|
configuration.GetSection("BasicSystemConfig").Bind(appSettings);
|
||||||
|
|
||||||
|
return appSettings.ThirdPdfUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+3
-3
@@ -135,7 +135,7 @@ public static class FileStoreHelper
|
|||||||
if (doc == null)
|
if (doc == null)
|
||||||
{
|
{
|
||||||
//---数据库没有找到对应的数据模板文件,请联系系统运维人员。
|
//---数据库没有找到对应的数据模板文件,请联系系统运维人员。
|
||||||
throw new BusinessValidationFailedException(StaticData.International("FileStore_TemplateFileNotFound"));
|
throw new BusinessValidationFailedException(I18n.T("FileStore_TemplateFileNotFound"));
|
||||||
}
|
}
|
||||||
|
|
||||||
var filePath = FileStoreHelper.GetPhysicalFilePath(_hostEnvironment, doc.Path);
|
var filePath = FileStoreHelper.GetPhysicalFilePath(_hostEnvironment, doc.Path);
|
||||||
@@ -143,7 +143,7 @@ public static class FileStoreHelper
|
|||||||
if (!System.IO.File.Exists(filePath))
|
if (!System.IO.File.Exists(filePath))
|
||||||
{
|
{
|
||||||
//---数据模板文件存储路径上未找对应文件,请联系系统运维人员。
|
//---数据模板文件存储路径上未找对应文件,请联系系统运维人员。
|
||||||
throw new BusinessValidationFailedException(StaticData.International("FileStore_TemplateFileStoragePathInvalid"));
|
throw new BusinessValidationFailedException(I18n.T("FileStore_TemplateFileStoragePathInvalid"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return (filePath, isEn_US ? doc.Name.Trim('/') : doc.NameCN.Trim('/'));
|
return (filePath, isEn_US ? doc.Name.Trim('/') : doc.NameCN.Trim('/'));
|
||||||
@@ -269,7 +269,7 @@ public static class FileStoreHelper
|
|||||||
{
|
{
|
||||||
|
|
||||||
//---解析Json文件配置出现问题
|
//---解析Json文件配置出现问题
|
||||||
throw new BusinessValidationFailedException(StaticData.International("SysMon_JsonConfig") + e.Message);
|
throw new BusinessValidationFailedException(I18n.T("SysMon_JsonConfig") + e.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
//默认存储的路径
|
//默认存储的路径
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
using Hangfire;
|
using Hangfire;
|
||||||
|
using IRaCIS.Core.Application.MassTransit.Consumer;
|
||||||
using IRaCIS.Core.Domain.Share;
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using MassTransit.Mediator;
|
||||||
|
|
||||||
namespace IRaCIS.Core.Application.Helper
|
namespace IRaCIS.Core.Application.Helper
|
||||||
{
|
{
|
||||||
@@ -62,15 +64,15 @@ namespace IRaCIS.Core.Application.Helper
|
|||||||
{
|
{
|
||||||
|
|
||||||
case EmailBusinessScenario.QCTask:
|
case EmailBusinessScenario.QCTask:
|
||||||
HangfireJobHelper.AddOrUpdateCronJob<IEmailSendService>(jobId, t => t.SendTrialImageQCTaskEmailAsync(trialId), emailCron);
|
|
||||||
break;
|
|
||||||
case EmailBusinessScenario.QCQuestion:
|
|
||||||
HangfireJobHelper.AddOrUpdateCronJob<IEmailSendService>(jobId, t => t.SendTrialQCQuestionEmailAsync(trialId), emailCron);
|
|
||||||
break;
|
|
||||||
case EmailBusinessScenario.ImageQuestion:
|
|
||||||
HangfireJobHelper.AddOrUpdateCronJob<IEmailSendService>(jobId, t => t.SendTrialImageQuestionAsync(trialId), emailCron);
|
|
||||||
break;
|
|
||||||
|
|
||||||
|
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new ImageQCRecurringEvent() { TrialId = trialId }, default), emailCron);
|
||||||
|
break;
|
||||||
|
case EmailBusinessScenario.CRCToQCQuestion:
|
||||||
|
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new CRCImageQuestionRecurringEvent() { TrialId = trialId }, default), emailCron);
|
||||||
|
break;
|
||||||
|
case EmailBusinessScenario.QCToCRCImageQuestion:
|
||||||
|
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new QCImageQuestionRecurringEvent() { TrialId = trialId }, default), emailCron);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,201 +4,134 @@ using IRaCIS.Core.Infrastructure;
|
|||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
using SharpCompress.Common;
|
||||||
|
|
||||||
namespace IRaCIS.Core.Application.Helper;
|
namespace IRaCIS.Core.Application.Helper;
|
||||||
|
|
||||||
|
|
||||||
|
public class IRCGlobalInfoDTO
|
||||||
|
{
|
||||||
|
public string Code { get; set; }
|
||||||
|
|
||||||
|
public string Value { get; set; }
|
||||||
|
|
||||||
|
public string ValueCN { get; set; }
|
||||||
|
|
||||||
|
public string Description { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public static class InternationalizationHelper
|
public static class InternationalizationHelper
|
||||||
{
|
{
|
||||||
public static string JsonFileFolder = Path.Combine(AppContext.BaseDirectory, StaticData.Folder.Resources);
|
public static string JsonFileFolder = Path.Combine(AppContext.BaseDirectory, StaticData.Folder.Resources);
|
||||||
|
|
||||||
public static FileSystemWatcher FileSystemWatcher_US { get; set; }
|
public static string USJsonPath = Path.Combine(JsonFileFolder, StaticData.En_US_Json);
|
||||||
public static FileSystemWatcher FileSystemWatcher_CN { get; set; }
|
public static string CNJsonPath = Path.Combine(JsonFileFolder, StaticData.Zh_CN_Json);
|
||||||
|
|
||||||
private static void VerifyFolder()
|
static InternationalizationHelper()
|
||||||
{
|
{
|
||||||
if (!Directory.Exists(JsonFileFolder) ||
|
if (!Directory.Exists(JsonFileFolder) ||
|
||||||
!Directory.GetFiles(JsonFileFolder).Any(filePath => Path.GetExtension(filePath).Equals(".json", StringComparison.OrdinalIgnoreCase)))
|
!Directory.GetFiles(JsonFileFolder).Any(filePath => Path.GetExtension(filePath).Equals(".json", StringComparison.OrdinalIgnoreCase)) ||
|
||||||
|
!File.Exists(USJsonPath) || !File.Exists(CNJsonPath))
|
||||||
{
|
{
|
||||||
throw new BusinessValidationFailedException("国际化Json文件目录有误");
|
throw new BusinessValidationFailedException(I18n.T("IRaCISCHangfireJob_FileNotFound"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task BatchAddJsonKeyValueAsync(List<BatchAddInternationalizationDto> batchAddDtos)
|
|
||||||
|
|
||||||
|
public static async Task BatchAddJsonKeyValueAsync(List<IRCGlobalInfoDTO> list)
|
||||||
{
|
{
|
||||||
VerifyFolder();
|
await StoreInfoToFileAsync(list);
|
||||||
|
}
|
||||||
|
|
||||||
var usJsonPath = Path.Combine(JsonFileFolder, StaticData.En_US_Json);
|
public static async Task AddOrUpdateJsonKeyValueAsync(IRCGlobalInfoDTO info)
|
||||||
var cnJsonPath = Path.Combine(JsonFileFolder, StaticData.Zh_CN_Json);
|
{
|
||||||
|
var list = new List<IRCGlobalInfoDTO>() { info };
|
||||||
|
|
||||||
|
await StoreInfoToFileAsync(list);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//更新json 文件 同时更新内存缓存的数据
|
public static async Task StoreInfoToFileAsync(List<IRCGlobalInfoDTO> list)
|
||||||
foreach (var filePath in new string[] { usJsonPath, cnJsonPath })
|
{
|
||||||
|
foreach (var filePath in new string[] { USJsonPath, CNJsonPath })
|
||||||
{
|
{
|
||||||
var json = await File.ReadAllTextAsync(filePath);
|
var json = await File.ReadAllTextAsync(filePath);
|
||||||
|
|
||||||
JObject jsonObject = JObject.Parse(json, new JsonLoadSettings() { CommentHandling = CommentHandling.Load });
|
JObject jsonObject = JObject.Parse(json, new JsonLoadSettings() { CommentHandling = CommentHandling.Load });
|
||||||
|
|
||||||
// 添加或更新指定的键值对
|
|
||||||
|
|
||||||
if (filePath.Contains(StaticData.En_US_Json))
|
if (filePath.Contains(StaticData.En_US_Json))
|
||||||
{
|
{
|
||||||
foreach (var item in batchAddDtos)
|
foreach (var tojsonItem in list)
|
||||||
{
|
{
|
||||||
jsonObject[item.Code] = item.Value;
|
jsonObject[tojsonItem.Code] = tojsonItem.Value;
|
||||||
|
|
||||||
StaticData.En_US_Dic[item.Code] = item.Value;
|
|
||||||
|
|
||||||
//日志记录该信息方便自己人看, 返回给客户的是配置的
|
//日志记录该信息方便自己人看, 返回给客户的是配置的
|
||||||
StaticData.Log_Locoalize_Dic[item.Code] = item.Description;
|
StaticData.Localizer_Dev_Dic[tojsonItem.Code] = tojsonItem.Description;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
foreach (var item in batchAddDtos)
|
foreach (var tojsonItem in list)
|
||||||
{
|
{
|
||||||
jsonObject[item.Code] = item.Value;
|
jsonObject[tojsonItem.Code] = tojsonItem.ValueCN;
|
||||||
|
|
||||||
StaticData.Zh_CN_Dic[item.Code] = item.Value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
await File.WriteAllTextAsync(filePath, jsonObject.ToString());
|
await File.WriteAllTextAsync(filePath, jsonObject.ToString());
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task AddOrUpdateJsonKeyValueAsync(string key, string value, string valueCN, string description)
|
|
||||||
{
|
|
||||||
|
|
||||||
VerifyFolder();
|
|
||||||
|
|
||||||
var usJsonPath = Path.Combine(JsonFileFolder, StaticData.En_US_Json);
|
|
||||||
var cnJsonPath = Path.Combine(JsonFileFolder, StaticData.Zh_CN_Json);
|
|
||||||
|
|
||||||
|
|
||||||
//更新json 文件 同时更新内存缓存的数据
|
#region 监测Json文件变更 实时刷新数据 废弃
|
||||||
foreach (var filePath in new string[] { usJsonPath, cnJsonPath })
|
//FileSystemWatcher_US = new FileSystemWatcher
|
||||||
{
|
//{
|
||||||
var json = await File.ReadAllTextAsync(filePath);
|
// Path = Path.GetDirectoryName(USJsonPath)!,
|
||||||
|
// NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size,
|
||||||
|
// Filter = Path.GetFileName(USJsonPath),
|
||||||
|
// EnableRaisingEvents = true,
|
||||||
|
|
||||||
JObject jsonObject = JObject.Parse(json, new JsonLoadSettings() { CommentHandling = CommentHandling.Load });
|
//};
|
||||||
|
//// 添加文件更改事件的处理程序
|
||||||
// 添加或更新指定的键值对
|
//FileSystemWatcher_US.Changed += (sender, e) => LoadJsonFile(StaticData.Folder.Resources + "\\" + StaticData.En_US_Json);
|
||||||
|
|
||||||
if (filePath.Contains(StaticData.En_US_Json))
|
|
||||||
{
|
|
||||||
jsonObject[key] = value;
|
|
||||||
|
|
||||||
StaticData.En_US_Dic[key] = value;
|
|
||||||
|
|
||||||
//日志记录该信息方便自己人看, 返回给客户的是配置的
|
|
||||||
StaticData.Log_Locoalize_Dic[key] = description;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
jsonObject[key] = valueCN;
|
|
||||||
|
|
||||||
StaticData.Zh_CN_Dic[key] = valueCN;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
await File.WriteAllTextAsync(filePath, jsonObject.ToString());
|
//FileSystemWatcher_CN = new FileSystemWatcher
|
||||||
|
//{
|
||||||
|
// Path = Path.GetDirectoryName(CNJsonPath)!,
|
||||||
|
// NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size,
|
||||||
|
// Filter = Path.GetFileName(CNJsonPath),
|
||||||
|
// EnableRaisingEvents = true,
|
||||||
|
|
||||||
}
|
//};
|
||||||
|
//FileSystemWatcher_CN.Changed += (sender, e) => LoadJsonFile(StaticData.Folder.Resources + "\\" + StaticData.Zh_CN_Json);
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static async Task InitInternationlizationDataAndWatchJsonFileAsync(IRepository<Internationalization> _internationalizationRepository)
|
//private static void LoadJsonFile(string filePath)
|
||||||
{
|
//{
|
||||||
//查询数据库的数据
|
// Console.WriteLine("刷新json内存数据");
|
||||||
var toJsonList = await _internationalizationRepository.Where(t => t.InternationalizationType == 1).Select(t => new
|
// IConfigurationBuilder builder = new ConfigurationBuilder().AddJsonFile(filePath, false, false);
|
||||||
{
|
|
||||||
t.Code,
|
|
||||||
t.Value,
|
|
||||||
t.ValueCN,
|
|
||||||
t.Description
|
|
||||||
}).ToListAsync();
|
|
||||||
|
|
||||||
//组织成json 文件
|
// IConfigurationRoot enConfiguration = builder.Build();
|
||||||
|
|
||||||
var usJsonPath = Path.Combine(JsonFileFolder, StaticData.En_US_Json);
|
// foreach (IConfigurationSection section in enConfiguration.GetChildren())
|
||||||
var cnJsonPath = Path.Combine(JsonFileFolder, StaticData.Zh_CN_Json);
|
// {
|
||||||
|
// if (filePath.Contains(StaticData.En_US_Json))
|
||||||
|
// {
|
||||||
|
// StaticData.En_US_Dic[section.Key] = section.Value;
|
||||||
|
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// StaticData.Zh_CN_Dic[section.Key] = section.Value;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
//本地静态文件国际化需要
|
#endregion
|
||||||
foreach (var tojsonItem in toJsonList)
|
|
||||||
{
|
|
||||||
StaticData.En_US_Dic[tojsonItem.Code] = tojsonItem.Value;
|
|
||||||
StaticData.Zh_CN_Dic[tojsonItem.Code] = tojsonItem.ValueCN;
|
|
||||||
|
|
||||||
//日志记录该信息方便自己人看, 返回给客户的是配置的
|
|
||||||
StaticData.Log_Locoalize_Dic[tojsonItem.Code] = tojsonItem.Description;
|
|
||||||
}
|
|
||||||
|
|
||||||
File.WriteAllText(usJsonPath, JsonConvert.SerializeObject(StaticData.En_US_Dic));
|
|
||||||
File.WriteAllText(cnJsonPath, JsonConvert.SerializeObject(StaticData.Zh_CN_Dic));
|
|
||||||
|
|
||||||
|
|
||||||
//监测Json文件变更 实时刷新数据
|
|
||||||
|
|
||||||
if (!File.Exists(usJsonPath) || !File.Exists(cnJsonPath))
|
|
||||||
{
|
|
||||||
throw new BusinessValidationFailedException(StaticData.International("IRaCISCHangfireJob_FileNotFound"));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// //监测Json文件变更 实时刷新数据
|
|
||||||
|
|
||||||
|
|
||||||
FileSystemWatcher_US = new FileSystemWatcher
|
|
||||||
{
|
|
||||||
Path = Path.GetDirectoryName(usJsonPath)!,
|
|
||||||
NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size,
|
|
||||||
Filter = Path.GetFileName(usJsonPath),
|
|
||||||
EnableRaisingEvents = true,
|
|
||||||
|
|
||||||
};
|
|
||||||
// 添加文件更改事件的处理程序
|
|
||||||
FileSystemWatcher_US.Changed += (sender, e) => LoadJsonFile(StaticData.Folder.Resources + "\\" + StaticData.En_US_Json);
|
|
||||||
|
|
||||||
|
|
||||||
FileSystemWatcher_CN = new FileSystemWatcher
|
|
||||||
{
|
|
||||||
Path = Path.GetDirectoryName(cnJsonPath)!,
|
|
||||||
NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size,
|
|
||||||
Filter = Path.GetFileName(cnJsonPath),
|
|
||||||
EnableRaisingEvents = true,
|
|
||||||
|
|
||||||
};
|
|
||||||
FileSystemWatcher_CN.Changed += (sender, e) => LoadJsonFile(StaticData.Folder.Resources + "\\" + StaticData.Zh_CN_Json);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static void LoadJsonFile(string filePath)
|
|
||||||
{
|
|
||||||
Console.WriteLine("刷新json内存数据");
|
|
||||||
IConfigurationBuilder builder = new ConfigurationBuilder().AddJsonFile(filePath, false, false);
|
|
||||||
|
|
||||||
IConfigurationRoot enConfiguration = builder.Build();
|
|
||||||
|
|
||||||
foreach (IConfigurationSection section in enConfiguration.GetChildren())
|
|
||||||
{
|
|
||||||
if (filePath.Contains(StaticData.En_US_Json))
|
|
||||||
{
|
|
||||||
StaticData.En_US_Dic[section.Key] = section.Value;
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
StaticData.Zh_CN_Dic[section.Key] = section.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Security;
|
||||||
|
using System.Net;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using RestSharp;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using IRaCIS.Core.Infrastructure.Extention;
|
||||||
|
using NPOI.SS.Formula.Functions;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.Application.Helper.OtherTool
|
||||||
|
{
|
||||||
|
public static class RestClientAPI
|
||||||
|
{
|
||||||
|
public static async Task<T> GetAsync<T>(string api, Dictionary<string, string> query = null, Dictionary<string, string> headers = null)
|
||||||
|
{
|
||||||
|
using var _client = new RestClient();
|
||||||
|
|
||||||
|
var request = new RestRequest(api, Method.Get);
|
||||||
|
|
||||||
|
if (query != null)
|
||||||
|
{
|
||||||
|
foreach (var kv in query)
|
||||||
|
{
|
||||||
|
request.AddParameter(kv.Key, kv.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (headers != null)
|
||||||
|
{
|
||||||
|
foreach (var header in headers)
|
||||||
|
{
|
||||||
|
request.AddHeader(header.Key, header.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var response = await _client.ExecuteAsync(request);
|
||||||
|
|
||||||
|
if (response.IsSuccessful)
|
||||||
|
{
|
||||||
|
return JsonConvert.DeserializeObject<T>(response.Content ?? string.Empty);
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine($"请求失败,错误代码: {response.StatusCode}, 错误消息: {response.ErrorMessage}");
|
||||||
|
return JsonConvert.DeserializeObject<T>(string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<T> PostAsync<T>(string api, object jsonObj = null, Dictionary<string, string> headers = null)
|
||||||
|
{
|
||||||
|
using var _client = new RestClient();
|
||||||
|
|
||||||
|
var request = new RestRequest(api, Method.Post);
|
||||||
|
|
||||||
|
if (jsonObj != null)
|
||||||
|
{
|
||||||
|
request.AddJsonBody(jsonObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (headers != null)
|
||||||
|
{
|
||||||
|
foreach (var header in headers)
|
||||||
|
{
|
||||||
|
request.AddHeader(header.Key, header.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var response = await _client.ExecuteAsync(request);
|
||||||
|
|
||||||
|
if (response.IsSuccessful)
|
||||||
|
{
|
||||||
|
return JsonConvert.DeserializeObject<T>(response.Content ?? string.Empty);
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine($"请求失败,错误代码: {response.StatusCode}, 错误消息: {response.ErrorMessage}");
|
||||||
|
return JsonConvert.DeserializeObject<T>(string.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,27 +26,16 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Remove="IRaCIS.Core.Application.xml" />
|
<None Remove="IRaCIS.Core.Application.xml" />
|
||||||
<None Remove="Resources\en-US.json" />
|
|
||||||
<None Remove="Resources\zh_ch.json" />
|
|
||||||
<None Remove="Service\Allocation\TaskConsistentRuleService.cs~RF1603d47.TMP" />
|
<None Remove="Service\Allocation\TaskConsistentRuleService.cs~RF1603d47.TMP" />
|
||||||
<None Remove="Service\Reading\ReadingImageTask\ReadingImageTaskService.cs~RF2f9323.TMP" />
|
<None Remove="Service\Reading\ReadingImageTask\ReadingImageTaskService.cs~RF2f9323.TMP" />
|
||||||
<None Remove="Service\Reading\ReadingImageTask\ReadingJudgeTaskService.cs~RF198afd23.TMP" />
|
<None Remove="Service\Reading\ReadingImageTask\ReadingJudgeTaskService.cs~RF198afd23.TMP" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Content Include="Resources\en-US.json">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
|
||||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
|
||||||
</Content>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.1.4" />
|
<PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.1.4" />
|
||||||
<PackageReference Include="AWSSDK.SecurityToken" Version="3.7.400.16" />
|
<PackageReference Include="AWSSDK.SecurityToken" Version="3.7.400.36" />
|
||||||
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" />
|
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" />
|
||||||
<PackageReference Include="AWSSDK.S3" Version="3.7.402.7" />
|
<PackageReference Include="AWSSDK.S3" Version="3.7.405" />
|
||||||
<PackageReference Include="DocX" Version="3.0.1" />
|
<PackageReference Include="DocX" Version="3.0.1" />
|
||||||
<PackageReference Include="FreeSpire.Doc" Version="12.2.0" />
|
<PackageReference Include="FreeSpire.Doc" Version="12.2.0" />
|
||||||
<PackageReference Include="Hangfire.Core" Version="1.8.14" />
|
<PackageReference Include="Hangfire.Core" Version="1.8.14" />
|
||||||
@@ -56,11 +45,13 @@
|
|||||||
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.5" />
|
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.5" />
|
||||||
<PackageReference Include="fo-dicom" Version="5.1.3" />
|
<PackageReference Include="fo-dicom" Version="5.1.3" />
|
||||||
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.1.3" />
|
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.1.3" />
|
||||||
<PackageReference Include="fo-dicom.Codecs" Version="5.14.5" />
|
<PackageReference Include="fo-dicom.Codecs" Version="5.15.1" />
|
||||||
<PackageReference Include="IP2Region.Net" Version="2.0.2" />
|
<PackageReference Include="IP2Region.Net" Version="2.0.2" />
|
||||||
<PackageReference Include="MailKit" Version="4.7.1.1" />
|
<PackageReference Include="MailKit" Version="4.7.1.1" />
|
||||||
|
<PackageReference Include="Masa.Contrib.Service.MinimalAPIs" Version="1.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.10" />
|
||||||
<PackageReference Include="MimeKit" Version="4.7.1" />
|
<PackageReference Include="MimeKit" Version="4.7.1" />
|
||||||
<PackageReference Include="MiniExcel" Version="1.34.1" />
|
<PackageReference Include="MiniExcel" Version="1.34.2" />
|
||||||
<PackageReference Include="Minio" Version="6.0.3" />
|
<PackageReference Include="Minio" Version="6.0.3" />
|
||||||
<PackageReference Include="MiniWord" Version="0.8.0" />
|
<PackageReference Include="MiniWord" Version="0.8.0" />
|
||||||
<PackageReference Include="My.Extensions.Localization.Json" Version="3.3.0">
|
<PackageReference Include="My.Extensions.Localization.Json" Version="3.3.0">
|
||||||
@@ -68,24 +59,14 @@
|
|||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="NPOI" Version="2.7.1" />
|
<PackageReference Include="NPOI" Version="2.7.1" />
|
||||||
<PackageReference Include="Panda.DynamicWebApi" Version="1.2.2" />
|
<PackageReference Include="Panda.DynamicWebApi" Version="1.2.2" />
|
||||||
<PackageReference Include="RestSharp" Version="112.0.0" />
|
<PackageReference Include="RestSharp" Version="112.1.0" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.5" />
|
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.5" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="8.0.2" />
|
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="8.0.2" />
|
||||||
<PackageReference Include="ZiggyCreatures.FusionCache" Version="1.3.0" />
|
<PackageReference Include="ZiggyCreatures.FusionCache" Version="1.4.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\IRaCIS.Core.Infra.EFCore\IRaCIS.Core.Infra.EFCore.csproj" />
|
<ProjectReference Include="..\IRaCIS.Core.Infra.EFCore\IRaCIS.Core.Infra.EFCore.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<None Update="Resources\ip2region.xdb">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="Triggers\TempAbandon\" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user