Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
commit
e666d93829
|
@ -16,12 +16,12 @@ namespace IRaCIS.Core.API
|
||||||
services.AddHangfire(hangFireConfig =>
|
services.AddHangfire(hangFireConfig =>
|
||||||
{
|
{
|
||||||
//本地window 调试 使用内存,服务器部署使用数据库,防止服务器任务调度到本地
|
//本地window 调试 使用内存,服务器部署使用数据库,防止服务器任务调度到本地
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
//if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
{
|
//{
|
||||||
hangFireConfig.UseInMemoryStorage();
|
// hangFireConfig.UseInMemoryStorage();
|
||||||
|
|
||||||
}
|
//}
|
||||||
else
|
//else
|
||||||
{
|
{
|
||||||
//指定存储介质
|
//指定存储介质
|
||||||
hangFireConfig.UseSqlServerStorage(hangFireConnStr, new SqlServerStorageOptions()
|
hangFireConfig.UseSqlServerStorage(hangFireConnStr, new SqlServerStorageOptions()
|
||||||
|
|
|
@ -66,5 +66,13 @@
|
||||||
"ApiPathList": [
|
"ApiPathList": [
|
||||||
"/test/get"
|
"/test/get"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"oauth": {
|
||||||
|
"github": {
|
||||||
|
"app_id": "github_app_id",
|
||||||
|
"app_key": "github_app_key",
|
||||||
|
"redirect_uri": "https://oauthlogin.net/oauth/githubcallback",
|
||||||
|
"scope": "repo"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -73,6 +73,12 @@ namespace IRaCIS.Core.Application.Helper
|
||||||
case EmailBusinessScenario.QCToCRCImageQuestion:
|
case EmailBusinessScenario.QCToCRCImageQuestion:
|
||||||
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new QCImageQuestionRecurringEvent() { TrialId = trialId }, default), emailCron);
|
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new QCImageQuestionRecurringEvent() { TrialId = trialId }, default), emailCron);
|
||||||
break;
|
break;
|
||||||
|
//加急阅片 10分钟
|
||||||
|
case EmailBusinessScenario.ExpeditedReading:
|
||||||
|
HangfireJobHelper.AddOrUpdateCronJob<IMediator>(jobId, t => t.Send(new UrgentIRUnReadTaskRecurringEvent() { TrialId = trialId }, default), emailCron);
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using IRaCIS.Application.Contracts;
|
using DocumentFormat.OpenXml;
|
||||||
|
using IRaCIS.Application.Contracts;
|
||||||
using IRaCIS.Core.Application.Contracts;
|
using IRaCIS.Core.Application.Contracts;
|
||||||
using IRaCIS.Core.Application.Helper;
|
using IRaCIS.Core.Application.Helper;
|
||||||
using IRaCIS.Core.Application.MassTransit.Consumer;
|
using IRaCIS.Core.Application.MassTransit.Consumer;
|
||||||
|
@ -43,7 +44,7 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||||
IsEn_US = isEn_US,
|
IsEn_US = isEn_US,
|
||||||
DictionaryList = new List<DictionaryDto>()
|
DictionaryList = new List<DictionaryDto>()
|
||||||
{
|
{
|
||||||
new DictionaryDto (){DictionaryCode= "YesOrNo",EnumValue="1" }
|
new DictionaryDto (){DictionaryCode= "YesOrNo",EnumValue="true" }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -66,16 +67,16 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||||
foreach (var trialUser in trialUserList)
|
foreach (var trialUser in trialUserList)
|
||||||
{
|
{
|
||||||
|
|
||||||
var userId=trialUser.UserId;
|
var userId = trialUser.UserId;
|
||||||
|
|
||||||
var doctorCriterionList = await _trialReadingCriterionRepository.Where(t => t.IsSigned && t.IsConfirm && t.Trial.TrialUserList.Any(t => t.UserId == userId))
|
var doctorCriterionList = await _trialReadingCriterionRepository.Where(t => t.IsSigned && t.IsConfirm && t.TrialId == trialId && t.Trial.TrialUserList.Any(t => t.UserId == userId))
|
||||||
.Select(c => new
|
.Select(c => new
|
||||||
{
|
{
|
||||||
|
|
||||||
TrialReadingCriterionId = c.Id,
|
TrialReadingCriterionId = c.Id,
|
||||||
CriterionName=c.CriterionName,
|
CriterionName = c.CriterionName,
|
||||||
|
|
||||||
UnReadList = c.VisitTaskList
|
UnReadList = c.VisitTaskList.Where(t => t.ExpetidEmailNoticeTime == null) //没有被通知
|
||||||
.Where(t => t.DoctorUserId == userId && t.ReadingTaskState != ReadingTaskState.HaveSigned && t.TaskState == TaskState.Effect)
|
.Where(t => t.DoctorUserId == userId && t.ReadingTaskState != ReadingTaskState.HaveSigned && t.TaskState == TaskState.Effect)
|
||||||
// 前序 不存在 未一致性核查未通过的
|
// 前序 不存在 未一致性核查未通过的
|
||||||
.Where(t => !t.Subject.SubjectVisitList.Any(sv => sv.CheckState != CheckStateEnum.CVPassed && t.VisitTaskNum > sv.VisitNum))
|
.Where(t => !t.Subject.SubjectVisitList.Any(sv => sv.CheckState != CheckStateEnum.CVPassed && t.VisitTaskNum > sv.VisitNum))
|
||||||
|
@ -84,69 +85,84 @@ namespace IRaCIS.Core.Application.MassTransit.Recurring
|
||||||
|
|
||||||
.Where(y => y.IsFrontTaskNeedSignButNotSign == false && (y.IsNeedClinicalDataSign == false || y.IsClinicalDataSign == true))
|
.Where(y => y.IsFrontTaskNeedSignButNotSign == false && (y.IsNeedClinicalDataSign == false || y.IsClinicalDataSign == true))
|
||||||
|
|
||||||
.Where(t=> t.SourceSubjectVisit.PDState==PDStateEnum.PDProgress || t.SourceSubjectVisit.IsEnrollmentConfirm ||
|
.Where(t => t.SourceSubjectVisit.PDState == PDStateEnum.PDProgress || t.SourceSubjectVisit.IsEnrollmentConfirm ||
|
||||||
t.ReadModule.SubjectVisit.PDState == PDStateEnum.PDProgress || t.ReadModule.SubjectVisit.IsEnrollmentConfirm)
|
t.ReadModule.SubjectVisit.PDState == PDStateEnum.PDProgress || t.ReadModule.SubjectVisit.IsEnrollmentConfirm)
|
||||||
.Select(c=>new
|
.Select(c => new
|
||||||
{
|
{
|
||||||
SubejctCode= c.IsAnalysisCreate? c.BlindSubjectCode: c.Subject.Code,
|
SubejctCode = c.IsAnalysisCreate ? c.BlindSubjectCode : c.Subject.Code,
|
||||||
c.TaskBlindName,
|
c.TaskBlindName,
|
||||||
c.TaskName
|
c.TaskName,
|
||||||
|
VisitTaskId = c.Id
|
||||||
}).ToList()
|
}).ToList()
|
||||||
|
|
||||||
}).ToListAsync();
|
}).ToListAsync();
|
||||||
|
|
||||||
|
|
||||||
var toTalUnreadCount= doctorCriterionList.SelectMany(t=>t.UnReadList).Count();
|
var toTalUnreadCount = doctorCriterionList.SelectMany(t => t.UnReadList).Count();
|
||||||
|
|
||||||
|
if (toTalUnreadCount > 0)
|
||||||
var messageToSend = new MimeMessage();
|
|
||||||
|
|
||||||
//发件地址
|
|
||||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
|
||||||
messageToSend.To.Add(new MailboxAddress(trialUser.FullName, trialUser.EMail));
|
|
||||||
|
|
||||||
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
|
||||||
|
|
||||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
|
||||||
{
|
{
|
||||||
var topicStr = string.Format(input.topicStr, trialUser.ResearchProgramNo);
|
var messageToSend = new MimeMessage();
|
||||||
|
|
||||||
var template = input.htmlBodyStr;
|
//发件地址
|
||||||
|
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||||
|
messageToSend.To.Add(new MailboxAddress(trialUser.FullName, trialUser.EMail));
|
||||||
|
|
||||||
//正则提取循环的部分 {%for%}(.*?){%end for%}
|
var companyName = isEn_US ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN;
|
||||||
string pattern = @"{%for%}(.*?){%end for%}";
|
|
||||||
var match = Regex.Match(template, pattern, RegexOptions.Singleline);
|
|
||||||
|
|
||||||
string criteriaTemplate = match.Groups[1].Value; // 提取循环模板
|
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||||
|
|
||||||
// 构建循环内容
|
|
||||||
string criteriaDetails = "";
|
|
||||||
foreach (var criteria in doctorCriterionList)
|
|
||||||
{
|
{
|
||||||
criteriaDetails += string.Format(criteriaTemplate, criteria.CriterionName, criteria.UnReadList.Select(t=>t.SubejctCode).Distinct().Count(), criteria.UnReadList.Count());
|
var topicStr = string.Format(input.topicStr, trialUser.ResearchProgramNo);
|
||||||
}
|
|
||||||
|
|
||||||
// 用循环内容替换原始模板中的循环部分
|
var template = input.htmlBodyStr;
|
||||||
string emailContent = Regex.Replace(template, pattern, criteriaDetails);
|
|
||||||
|
//正则提取循环的部分 {%for%}(.*?){%end for%}
|
||||||
|
string pattern = @"{%for%}(.*?){%end for%}";
|
||||||
|
var match = Regex.Match(template, pattern, RegexOptions.Singleline);
|
||||||
|
|
||||||
|
string criteriaTemplate = match.Groups[1].Value; // 提取循环模板
|
||||||
|
|
||||||
|
// 构建循环内容
|
||||||
|
string criteriaDetails = "";
|
||||||
|
foreach (var criteria in doctorCriterionList)
|
||||||
|
{
|
||||||
|
if (criteria.UnReadList.Count() > 0)
|
||||||
|
{
|
||||||
|
criteriaDetails += string.Format(criteriaTemplate, criteria.CriterionName, criteria.UnReadList.Select(t => t.SubejctCode).Distinct().Count(), criteria.UnReadList.Count());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用循环内容替换原始模板中的循环部分
|
||||||
|
string emailContent = template.Replace(criteriaTemplate, criteriaDetails).Replace("{%for%}", "").Replace("{%end for%}", "");
|
||||||
|
|
||||||
|
|
||||||
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, input.htmlBodyStr),
|
|
||||||
trialUser.FullName,
|
|
||||||
|
|
||||||
DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
|
var htmlBodyStr = string.Format(CommonEmailHelper.ReplaceCompanyName(_systemEmailConfig, emailContent),
|
||||||
toTalUnreadCount,
|
trialUser.FullName,
|
||||||
|
|
||||||
trialUser.ResearchProgramNo,
|
DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
|
||||||
dictionValue[0],
|
toTalUnreadCount,
|
||||||
_systemEmailConfig.SiteUrl
|
|
||||||
);
|
|
||||||
|
|
||||||
return (topicStr, htmlBodyStr);
|
trialUser.ResearchProgramNo,
|
||||||
};
|
dictionValue[0],
|
||||||
|
_systemEmailConfig.SiteUrl
|
||||||
|
);
|
||||||
|
|
||||||
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(_emailNoticeConfigrepository, EmailBusinessScenario.ExpeditedReading, messageToSend, emailConfigFunc);
|
return (topicStr, htmlBodyStr);
|
||||||
|
};
|
||||||
|
|
||||||
|
await CommonEmailHelper.GetEmailSubejctAndHtmlInfoAndBuildAsync(_emailNoticeConfigrepository, EmailBusinessScenario.ExpeditedReading, messageToSend, emailConfigFunc);
|
||||||
|
|
||||||
|
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
||||||
|
|
||||||
|
//处理标记已通知的任务
|
||||||
|
|
||||||
|
var visitTaskIdList = doctorCriterionList.Where(t => t.UnReadList.Count() > 0).SelectMany(t => t.UnReadList.Select(u => u.VisitTaskId)).ToList();
|
||||||
|
|
||||||
|
await _visitTaskRepository.BatchUpdateNoTrackingAsync(t => visitTaskIdList.Contains(t.Id), u => new VisitTask() { ExpetidEmailNoticeTime = DateTime.Now });
|
||||||
|
}
|
||||||
|
|
||||||
await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1757,8 +1757,8 @@ namespace IRaCIS.Core.Application.Service.Common
|
||||||
var query = _visitTaskRepository.Where(t => t.TrialId == inQuery.TrialId && (t.TaskState == TaskState.Effect || t.TaskState == TaskState.Freeze))
|
var query = _visitTaskRepository.Where(t => t.TrialId == inQuery.TrialId && (t.TaskState == TaskState.Effect || t.TaskState == TaskState.Freeze))
|
||||||
|
|
||||||
//自身一致性分析
|
//自身一致性分析
|
||||||
.WhereIf(inQuery.ReadingExportType == ExportResult.SelfAnalysis, t => t.IsSelfAnalysis == true || t.IsSelfAnalysis == null)
|
.WhereIf(inQuery.ReadingExportType == ExportResult.DetailedTableOfIntraReaderAnalysisResults, t => t.IsSelfAnalysis == true || t.IsSelfAnalysis == null)
|
||||||
.WhereIf(inQuery.ReadingExportType == ExportResult.GroupAnalysis, t => t.IsSelfAnalysis == false || t.IsSelfAnalysis == null)
|
.WhereIf(inQuery.ReadingExportType == ExportResult.DetailedTableOfInterReaderAnalysisResults, t => t.IsSelfAnalysis == false || t.IsSelfAnalysis == null)
|
||||||
|
|
||||||
//访视和全局查询已签名完成的,裁判可以是未签名,未完成的
|
//访视和全局查询已签名完成的,裁判可以是未签名,未完成的
|
||||||
.Where(t => (t.ReadingTaskState == ReadingTaskState.HaveSigned && (t.ReadingCategory == ReadingCategory.Visit || t.ReadingCategory == ReadingCategory.Global)) || t.ReadingCategory == ReadingCategory.Judge)
|
.Where(t => (t.ReadingTaskState == ReadingTaskState.HaveSigned && (t.ReadingCategory == ReadingCategory.Visit || t.ReadingCategory == ReadingCategory.Global)) || t.ReadingCategory == ReadingCategory.Judge)
|
||||||
|
@ -1859,7 +1859,7 @@ namespace IRaCIS.Core.Application.Service.Common
|
||||||
|
|
||||||
var export_Template = StaticData.Export.TrialSelfAnalysisList_Export;
|
var export_Template = StaticData.Export.TrialSelfAnalysisList_Export;
|
||||||
|
|
||||||
if (inQuery.ReadingExportType == ExportResult.SelfAnalysis)
|
if (inQuery.ReadingExportType == ExportResult.DetailedTableOfIntraReaderAnalysisResults)
|
||||||
{
|
{
|
||||||
//找到非一致性分析的任务
|
//找到非一致性分析的任务
|
||||||
var selfExportList = list.Where(t => t.IsSelfAnalysis == null).ToList();
|
var selfExportList = list.Where(t => t.IsSelfAnalysis == null).ToList();
|
||||||
|
@ -1956,7 +1956,7 @@ namespace IRaCIS.Core.Application.Service.Common
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
var (memoryStream, fileName) = await ExcelExportHelper.DataExport_NpoiTestAsync(export_Template, exportInfo, _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(CommonEvaluationExport), criterion.CriterionType, dynamicColumnConfig);
|
var (memoryStream, fileName) = await ExcelExportHelper.DataExport_NpoiTestAsync(export_Template, exportInfo, _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(AnalysisDynamicCommonExport), criterion.CriterionType, dynamicColumnConfig);
|
||||||
|
|
||||||
return new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
return new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
||||||
{
|
{
|
||||||
|
|
|
@ -325,7 +325,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||||||
if (findStudy.SubejectCode == inCommand.SubjectCode && findStudy.SubjectId == inCommand.SubjectId)
|
if (findStudy.SubejectCode == inCommand.SubjectCode && findStudy.SubjectId == inCommand.SubjectId)
|
||||||
{
|
{
|
||||||
//上传给后处理其他访视了
|
//上传给后处理其他访视了
|
||||||
if (inCommand.VisitTaskId != null && findStudy.VisitTaskId != null && findStudy.VisitTaskId != inCommand.VisitTaskId)
|
if (inCommand.VisitTaskId != null && findStudy.VisitTaskId != null && findStudy.VisitTaskId != inCommand.VisitTaskId && findStudy.SubjectVisitId != currentUploadSubjectVisitId)
|
||||||
{
|
{
|
||||||
result.Add(new TaskStudyArchiveConfirmResult() { StudyInstanceUid = studyUid, IsAllowReUpload = false, IsAllowUpload = false });
|
result.Add(new TaskStudyArchiveConfirmResult() { StudyInstanceUid = studyUid, IsAllowReUpload = false, IsAllowUpload = false });
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Text.Json.Serialization;
|
using Newtonsoft.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace IRaCIS.Core.Application.Service.OAuth;
|
namespace IRaCIS.Core.Application.Service.OAuth;
|
||||||
|
|
||||||
|
@ -8,30 +9,35 @@ public class LogtoTokenResponse
|
||||||
/// The access token issued by the Logto authorization server.
|
/// The access token issued by the Logto authorization server.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonPropertyName("access_token")]
|
[JsonPropertyName("access_token")]
|
||||||
|
[JsonProperty("access_token")]
|
||||||
public string AccessToken { get; set; } = null!;
|
public string AccessToken { get; set; } = null!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The type of the token issued by the Logto authorization server.
|
/// The type of the token issued by the Logto authorization server.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonPropertyName("token_type")]
|
[JsonPropertyName("token_type")]
|
||||||
|
[JsonProperty("token_type")]
|
||||||
public string TokenType { get; set; } = null!;
|
public string TokenType { get; set; } = null!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The lifetime in seconds of the access token.
|
/// The lifetime in seconds of the access token.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonPropertyName("expires_in")]
|
[JsonPropertyName("expires_in")]
|
||||||
|
[JsonProperty("expires_in")]
|
||||||
public int ExpiresIn { get; set; }
|
public int ExpiresIn { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The refresh token, which can be used to obtain new access tokens using the same authorization grant.
|
/// The refresh token, which can be used to obtain new access tokens using the same authorization grant.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonPropertyName("refresh_token")]
|
[JsonPropertyName("refresh_token")]
|
||||||
|
[JsonProperty("refresh_token")]
|
||||||
public string? RefreshToken { get; set; } = null!;
|
public string? RefreshToken { get; set; } = null!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The ID token, which can be used to verify the identity of the user.
|
/// The ID token, which can be used to verify the identity of the user.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonPropertyName("id_token")]
|
[JsonPropertyName("id_token")]
|
||||||
|
[JsonProperty("id_token")]
|
||||||
public string? IdToken { get; set; } = null;
|
public string? IdToken { get; set; } = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,20 @@
|
||||||
using IdentityModel.Client;
|
using Azure.Core;
|
||||||
|
using IdentityModel;
|
||||||
|
using IdentityModel.Client;
|
||||||
using IRaCIS.Core.Application.Service.OAuth;
|
using IRaCIS.Core.Application.Service.OAuth;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using NPOI.SS.Formula.Functions;
|
||||||
|
using Org.BouncyCastle.Utilities.Net;
|
||||||
using RestSharp;
|
using RestSharp;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
@ -16,6 +25,360 @@ namespace IRaCIS.Core.Application.Service
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region authorization_code 原生 PKCE
|
||||||
|
|
||||||
|
public IResponseOutput TestPCKEOrgin()
|
||||||
|
{
|
||||||
|
// 1. 生成 code_verifier 和 code_challenge
|
||||||
|
string codeVerifier = "QMSBBxTQrpKPscvNNfmaQfmyk5Wd33GZS1FKSo3Shv8w-59vW1iTSlgAznYojkYv2DgR4XhTqySsBnDPq0";
|
||||||
|
|
||||||
|
|
||||||
|
string codeChallenge = PkceUtil.GenerateCodeChallenge(codeVerifier);
|
||||||
|
|
||||||
|
Console.WriteLine(codeVerifier);
|
||||||
|
|
||||||
|
string clientId = "aj34vqrpvz8olsbxwtcog";
|
||||||
|
string redirectUri = "http://localhost:6100/OAuth/TestPKCECallBack";
|
||||||
|
string state = "123456";
|
||||||
|
|
||||||
|
// 构造请求的 URL
|
||||||
|
string authorizationUrl = $"https://logto.test.extimaging.com/oidc/auth" +
|
||||||
|
$"?client_id={clientId}" +
|
||||||
|
$"&redirect_uri={Uri.EscapeDataString(redirectUri)}" +
|
||||||
|
$"&response_type=code" +
|
||||||
|
$"&scope=openid profile email phone" +
|
||||||
|
$"&code_challenge={codeChallenge}" +
|
||||||
|
$"&code_challenge_method=S256" +
|
||||||
|
$"&state={state}";
|
||||||
|
|
||||||
|
Console.WriteLine("请将以下 URL 复制到浏览器中,完成登录后获取 code:");
|
||||||
|
|
||||||
|
Console.WriteLine(authorizationUrl);
|
||||||
|
|
||||||
|
return ResponseOutput.Ok();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[AllowAnonymous]
|
||||||
|
[RoutePattern(HttpMethod = "Get")]
|
||||||
|
public async Task<IResponseOutput> TestPKCECallBackAsync(string code)
|
||||||
|
{
|
||||||
|
string codeVerifier = "QMSBBxTQrpKPscvNNfmaQfmyk5Wd33GZS1FKSo3Shv8w-59vW1iTSlgAznYojkYv2DgR4XhTqySsBnDPq0";
|
||||||
|
// OIDC 配置,替换为您的 OIDC 提供者的配置
|
||||||
|
string tokenEndpoint = "https://logto.test.extimaging.com/oidc/token"; // 替换为实际 token 端点
|
||||||
|
string clientId = "aj34vqrpvz8olsbxwtcog";
|
||||||
|
string redirectUri = "http://localhost:6100/OAuth/TestPKCECallBack"; // 替换为前端的回调 URL
|
||||||
|
|
||||||
|
var baseUrl = "https://logto.test.extimaging.com";
|
||||||
|
var opts = new RestClientOptions(baseUrl);
|
||||||
|
using var client = new RestClient(opts);
|
||||||
|
|
||||||
|
//https://blog.logto.io/troubleshoot-invalid-grant-error/
|
||||||
|
var request = new RestRequest("oidc/token", Method.Post)
|
||||||
|
.AddHeader("Content-Type", "application/x-www-form-urlencoded");
|
||||||
|
|
||||||
|
request.AddParameter("grant_type", "authorization_code")
|
||||||
|
.AddParameter("code", code)
|
||||||
|
.AddParameter("redirect_uri", redirectUri)
|
||||||
|
.AddParameter("client_id", clientId)
|
||||||
|
.AddParameter("code_verifier", codeVerifier); // 使用 PKCE
|
||||||
|
|
||||||
|
|
||||||
|
// 发送请求并获取响应
|
||||||
|
var response = await client.ExecuteAsync<LogtoTokenResponse>(request);
|
||||||
|
|
||||||
|
if (response.StatusCode == HttpStatusCode.OK)
|
||||||
|
{
|
||||||
|
var tokenResponse = response.Data;
|
||||||
|
|
||||||
|
Console.WriteLine(tokenResponse.ToJsonStr());
|
||||||
|
|
||||||
|
var userInfoRequest = new RestRequest($"oidc/me", Method.Get)
|
||||||
|
.AddHeader("Authorization", $"Bearer {tokenResponse.AccessToken}");
|
||||||
|
|
||||||
|
var userResponse = await client.ExecuteAsync(userInfoRequest);
|
||||||
|
|
||||||
|
Console.WriteLine(userResponse.Content);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResponseOutput.Ok();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region authorization_code OidcClient PKCE
|
||||||
|
|
||||||
|
[AllowAnonymous]
|
||||||
|
[RoutePattern(HttpMethod = "Get")]
|
||||||
|
public IResponseOutput TestPKCE()
|
||||||
|
{
|
||||||
|
// 1. 生成 code_verifier 和 code_challenge
|
||||||
|
string codeVerifier = "QMSBBxTQrpKPscvNNfmaQfmyk5Wd33GZS1FKSo3Shv8w-59vW1iTSlgAznYojkYv2DgR4XhTqySsBnDPq0";
|
||||||
|
|
||||||
|
|
||||||
|
string codeChallenge = PkceUtil.GenerateCodeChallenge(codeVerifier);
|
||||||
|
|
||||||
|
Console.WriteLine(codeVerifier);
|
||||||
|
|
||||||
|
string clientId = "aj34vqrpvz8olsbxwtcog";
|
||||||
|
string redirectUri = "http://localhost:6100/OAuth/TestOidcClientPKCECallBack";
|
||||||
|
string state = "123456";
|
||||||
|
|
||||||
|
// 构造请求的 URL
|
||||||
|
string authorizationUrl = $"https://logto.test.extimaging.com/oidc/auth" +
|
||||||
|
$"?client_id={clientId}" +
|
||||||
|
$"&redirect_uri={Uri.EscapeDataString(redirectUri)}" +
|
||||||
|
$"&response_type=code" +
|
||||||
|
$"&scope=openid profile email phone" +
|
||||||
|
$"&code_challenge={codeChallenge}" +
|
||||||
|
$"&code_challenge_method=S256" +
|
||||||
|
$"&state={state}";
|
||||||
|
|
||||||
|
Console.WriteLine("请将以下 URL 复制到浏览器中,完成登录后获取 code:");
|
||||||
|
|
||||||
|
Console.WriteLine(authorizationUrl);
|
||||||
|
|
||||||
|
return ResponseOutput.Ok();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[AllowAnonymous]
|
||||||
|
[RoutePattern(HttpMethod = "Get")]
|
||||||
|
public async Task<IResponseOutput> TestOidcClientPKCECallBackAsync(string code)
|
||||||
|
{
|
||||||
|
//使用IdentityModel.OidcClient 测试
|
||||||
|
var client = new HttpClient();
|
||||||
|
var disco = await client.GetDiscoveryDocumentAsync("https://logto.test.extimaging.com/oidc");
|
||||||
|
if (disco.IsError)
|
||||||
|
{
|
||||||
|
Console.WriteLine(disco.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// OIDC 配置,替换为您的 OIDC 提供者的配置
|
||||||
|
string clientId = "aj34vqrpvz8olsbxwtcog";
|
||||||
|
string codeVerifier = "QMSBBxTQrpKPscvNNfmaQfmyk5Wd33GZS1FKSo3Shv8w-59vW1iTSlgAznYojkYv2DgR4XhTqySsBnDPq0";
|
||||||
|
string redirectUri = "http://localhost:6100/OAuth/TestOidcClientPKCECallBack"; // 替换为前端的回调 URL
|
||||||
|
|
||||||
|
var requestBody = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "grant_type", "authorization_code" },
|
||||||
|
{ "code", code },
|
||||||
|
{ "redirect_uri", redirectUri },
|
||||||
|
{ "client_id", clientId },
|
||||||
|
{ "code_verifier", codeVerifier } // 使用 PKCE
|
||||||
|
};
|
||||||
|
|
||||||
|
var _httpClient = new HttpClient();
|
||||||
|
var content = new FormUrlEncodedContent(requestBody);
|
||||||
|
// 发出 token 请求
|
||||||
|
var response = await _httpClient.PostAsync(disco.TokenEndpoint, content);
|
||||||
|
|
||||||
|
|
||||||
|
if (response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
var responseBody = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
// 解析 JSON
|
||||||
|
var jsonObject = JObject.Parse(responseBody);
|
||||||
|
|
||||||
|
// 格式化并输出 JSON
|
||||||
|
var formattedJson = jsonObject.ToString(Formatting.Indented);
|
||||||
|
|
||||||
|
Console.WriteLine(formattedJson);
|
||||||
|
|
||||||
|
var tokenResponse=JsonConvert.DeserializeObject<LogtoTokenResponse>(responseBody);
|
||||||
|
|
||||||
|
Console.WriteLine(tokenResponse);
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var errorContent = await response.Content.ReadAsStringAsync();
|
||||||
|
throw new Exception($"Error: {errorContent}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#region 提示必须要Secret
|
||||||
|
//// 准备请求内容
|
||||||
|
//var tokenRequest = new AuthorizationCodeTokenRequest
|
||||||
|
//{
|
||||||
|
// Address = disco.TokenEndpoint,
|
||||||
|
// ClientId = clientId,
|
||||||
|
// Code = code,
|
||||||
|
// RedirectUri = redirectUri,
|
||||||
|
// GrantType = "authorization_code",
|
||||||
|
// CodeVerifier = codeVerifier
|
||||||
|
|
||||||
|
//};
|
||||||
|
|
||||||
|
//var tokenResponse = await _httpClient.RequestTokenAsync(tokenRequest);
|
||||||
|
|
||||||
|
//if (tokenResponse.HttpStatusCode == HttpStatusCode.OK)
|
||||||
|
//{
|
||||||
|
// var apiClient = new HttpClient();
|
||||||
|
// apiClient.SetBearerToken(tokenResponse.AccessToken);
|
||||||
|
|
||||||
|
// var response = await apiClient.GetAsync(disco.UserInfoEndpoint);
|
||||||
|
// if (!response.IsSuccessStatusCode)
|
||||||
|
// {
|
||||||
|
// Console.WriteLine(response.StatusCode);
|
||||||
|
// Console.WriteLine(response.ReasonPhrase);
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// var doc = JsonDocument.Parse(await response.Content.ReadAsStringAsync()).RootElement;
|
||||||
|
// Console.WriteLine(JsonSerializer.Serialize(doc, new JsonSerializerOptions { WriteIndented = true }));
|
||||||
|
|
||||||
|
// //获取刷新token
|
||||||
|
// var refreshClient = new HttpClient();
|
||||||
|
// var refreshRequest = new RefreshTokenRequest
|
||||||
|
// {
|
||||||
|
// Address = disco.TokenEndpoint,
|
||||||
|
// ClientId = clientId,
|
||||||
|
// RefreshToken = tokenResponse.RefreshToken,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// var refreshResponse = await refreshClient.RequestRefreshTokenAsync(refreshRequest);
|
||||||
|
|
||||||
|
// if (refreshResponse.IsError)
|
||||||
|
// {
|
||||||
|
// Console.WriteLine($"Error: {refreshResponse.Error}");
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// Console.WriteLine("获取刷新token 完成");
|
||||||
|
|
||||||
|
// Console.WriteLine("AccessToken:" + refreshResponse.AccessToken);
|
||||||
|
|
||||||
|
// Console.WriteLine("RefreshToken:" + refreshResponse.RefreshToken);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return ResponseOutput.Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region OIDC authorization_code with client_secret
|
||||||
|
|
||||||
|
[AllowAnonymous]
|
||||||
|
public IResponseOutput TestOidcClientWithSecret()
|
||||||
|
{
|
||||||
|
|
||||||
|
string clientId = "tl42rjin7obxtwqqgvkti";
|
||||||
|
string redirectUri = "http://localhost:6100/OAuth/TestOidcClientCallBack";
|
||||||
|
string state = "123456";
|
||||||
|
|
||||||
|
// 构造请求的 URL
|
||||||
|
string authorizationUrl = $"https://logto.test.extimaging.com/oidc/auth" +
|
||||||
|
$"?client_id={clientId}" +
|
||||||
|
$"&redirect_uri={Uri.EscapeDataString(redirectUri)}" +
|
||||||
|
$"&response_type=code" +
|
||||||
|
$"&scope=openid profile email phone" +
|
||||||
|
$"&state={state}";
|
||||||
|
|
||||||
|
Console.WriteLine(authorizationUrl);
|
||||||
|
|
||||||
|
return ResponseOutput.Ok();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[AllowAnonymous]
|
||||||
|
[RoutePattern(HttpMethod = "Get")]
|
||||||
|
public async Task<IResponseOutput> TestOidcClientCallBackAsync(string code)
|
||||||
|
{
|
||||||
|
//使用IdentityModel.OidcClient 测试
|
||||||
|
var client = new HttpClient();
|
||||||
|
var disco = await client.GetDiscoveryDocumentAsync("https://logto.test.extimaging.com/oidc");
|
||||||
|
if (disco.IsError)
|
||||||
|
{
|
||||||
|
Console.WriteLine(disco.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// OIDC 配置,替换为您的 OIDC 提供者的配置
|
||||||
|
string clientId = "tl42rjin7obxtwqqgvkti";
|
||||||
|
string clientSecret = "Pu9ig4rz44aLlxb0yKUaOiZaFk6Bcu51";
|
||||||
|
string redirectUri = "http://localhost:6100/OAuth/TestOidcClientCallBack"; // 替换为前端的回调 URL
|
||||||
|
// 准备请求内容
|
||||||
|
var tokenRequest = new AuthorizationCodeTokenRequest
|
||||||
|
{
|
||||||
|
Address = disco.TokenEndpoint,
|
||||||
|
ClientId = clientId,
|
||||||
|
ClientSecret = clientSecret,
|
||||||
|
Code = code,
|
||||||
|
RedirectUri = redirectUri,
|
||||||
|
GrantType = "authorization_code",
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
var _httpClient = new HttpClient();
|
||||||
|
// 发出 token 请求
|
||||||
|
var tokenResponse = await _httpClient.RequestAuthorizationCodeTokenAsync(tokenRequest);
|
||||||
|
|
||||||
|
|
||||||
|
if (tokenResponse.HttpStatusCode == HttpStatusCode.OK)
|
||||||
|
{
|
||||||
|
var apiClient = new HttpClient();
|
||||||
|
apiClient.SetBearerToken(tokenResponse.AccessToken);
|
||||||
|
|
||||||
|
var response = await apiClient.GetAsync(disco.UserInfoEndpoint);
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
Console.WriteLine(response.StatusCode);
|
||||||
|
Console.WriteLine(response.ReasonPhrase);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var doc = JsonDocument.Parse(await response.Content.ReadAsStringAsync()).RootElement;
|
||||||
|
Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(doc, new JsonSerializerOptions { WriteIndented = true }));
|
||||||
|
|
||||||
|
//获取刷新token
|
||||||
|
var refreshClient = new HttpClient();
|
||||||
|
var refreshRequest = new RefreshTokenRequest
|
||||||
|
{
|
||||||
|
Address = disco.TokenEndpoint,
|
||||||
|
ClientId = clientId,
|
||||||
|
ClientSecret = clientSecret,
|
||||||
|
RefreshToken = tokenResponse.RefreshToken,
|
||||||
|
};
|
||||||
|
|
||||||
|
var refreshResponse = await refreshClient.RequestRefreshTokenAsync(refreshRequest);
|
||||||
|
|
||||||
|
if (refreshResponse.IsError)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Error: {refreshResponse.Error}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("获取刷新token 完成");
|
||||||
|
|
||||||
|
Console.WriteLine("AccessToken:" + refreshResponse.AccessToken);
|
||||||
|
|
||||||
|
Console.WriteLine("RefreshToken:" + refreshResponse.RefreshToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResponseOutput.Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region 客户端凭证
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 测试客户端凭证代码
|
/// 测试客户端凭证代码
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -66,7 +429,7 @@ namespace IRaCIS.Core.Application.Service
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var doc = JsonDocument.Parse(await response.Content.ReadAsStringAsync()).RootElement;
|
var doc = JsonDocument.Parse(await response.Content.ReadAsStringAsync()).RootElement;
|
||||||
Console.WriteLine(JsonSerializer.Serialize(doc, new JsonSerializerOptions { WriteIndented = true }));
|
Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(doc, new JsonSerializerOptions { WriteIndented = true }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,5 +505,37 @@ namespace IRaCIS.Core.Application.Service
|
||||||
|
|
||||||
return ResponseOutput.Ok();
|
return ResponseOutput.Ok();
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PkceUtil
|
||||||
|
{
|
||||||
|
// 生成 code_verifier
|
||||||
|
public static string GenerateCodeVerifier()
|
||||||
|
{
|
||||||
|
var bytes = new byte[64];
|
||||||
|
using (var random = RandomNumberGenerator.Create())
|
||||||
|
{
|
||||||
|
random.GetBytes(bytes);
|
||||||
|
return Convert.ToBase64String(bytes)
|
||||||
|
.TrimEnd('=')
|
||||||
|
.Replace('+', '-')
|
||||||
|
.Replace('/', '_');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成 code_challenge
|
||||||
|
public static string GenerateCodeChallenge(string codeVerifier)
|
||||||
|
{
|
||||||
|
using (var sha256 = SHA256.Create())
|
||||||
|
{
|
||||||
|
var bytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(codeVerifier));
|
||||||
|
return Convert.ToBase64String(bytes)
|
||||||
|
.TrimEnd('=')
|
||||||
|
.Replace('+', '-')
|
||||||
|
.Replace('/', '_');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -472,11 +472,6 @@ namespace IRaCIS.Core.Domain.Share
|
||||||
//阅片期一致率
|
//阅片期一致率
|
||||||
ReadingPeriodJudgeRatio_Export = 8,
|
ReadingPeriodJudgeRatio_Export = 8,
|
||||||
|
|
||||||
SelfAnalysis = 9,
|
|
||||||
|
|
||||||
GroupAnalysis = 10
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -278,4 +278,7 @@ public class VisitTask : BaseFullAuditEntity
|
||||||
[Comment("前序任务需要签名 但是未签名")]
|
[Comment("前序任务需要签名 但是未签名")]
|
||||||
public bool IsFrontTaskNeedSignButNotSign { get; set; }
|
public bool IsFrontTaskNeedSignButNotSign { get; set; }
|
||||||
public int ImageStudyState { get; set; }
|
public int ImageStudyState { get; set; }
|
||||||
|
|
||||||
|
[Comment("通知IR加急阅片时间")]
|
||||||
|
public DateTime? ExpetidEmailNoticeTime { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,6 +99,8 @@ public static class DBContext_Ext
|
||||||
var delaySeconds = dbContext.TrialEmailNoticeConfig.Where(t => t.BusinessScenarioEnum == businessEnum)
|
var delaySeconds = dbContext.TrialEmailNoticeConfig.Where(t => t.BusinessScenarioEnum == businessEnum)
|
||||||
.Select(t => t.EmailDelaySeconds).FirstOrDefault();
|
.Select(t => t.EmailDelaySeconds).FirstOrDefault();
|
||||||
|
|
||||||
|
Console.WriteLine("qc领取任务:" + DateTime.Now.ToShortTimeString() + $"延时{delaySeconds}");
|
||||||
|
|
||||||
subjectVisit.AddDomainEvent(new QCClaimTaskEvent() { IsPd = subjectVisit.PDState == PDStateEnum.PDProgress, SubjectVisitId = subjectVisit.Id, DelaySeconds = delaySeconds, CurrentActionUserId = (Guid)subjectVisit.CurrentActionUserId });
|
subjectVisit.AddDomainEvent(new QCClaimTaskEvent() { IsPd = subjectVisit.PDState == PDStateEnum.PDProgress, SubjectVisitId = subjectVisit.Id, DelaySeconds = delaySeconds, CurrentActionUserId = (Guid)subjectVisit.CurrentActionUserId });
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,11 +60,16 @@ namespace IRaCIS.Core.Infra.EFCore.Interceptor
|
||||||
|
|
||||||
if (domainEvent.IsDelayScheduleEvent)
|
if (domainEvent.IsDelayScheduleEvent)
|
||||||
{
|
{
|
||||||
|
Console.WriteLine("qc领取任务消息发布:" + DateTime.Now.ToShortTimeString() + $"延时{domainEvent.DelaySeconds}");
|
||||||
//延迟调度的消息,比如1h后再消费
|
//延迟调度的消息,比如1h后再消费
|
||||||
await _scheduler.SchedulePublish(DateTime.Now.AddSeconds((int)domainEvent.DelaySeconds!), (object)domainEvent);
|
await _scheduler.SchedulePublish(DateTime.Now.AddSeconds((int)domainEvent.DelaySeconds!), (object)domainEvent);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await _publishEndpoint.Publish(domainEvent.GetType(), domainEvent);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
await _publishEndpoint.Publish(domainEvent.GetType(), domainEvent);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
18050
IRaCIS.Core.Infra.EFCore/Migrations/20241101061056_VisitTaskExpetiedTime.Designer.cs
generated
Normal file
18050
IRaCIS.Core.Infra.EFCore/Migrations/20241101061056_VisitTaskExpetiedTime.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,30 @@
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.Infra.EFCore.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class VisitTaskExpetiedTime : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<DateTime>(
|
||||||
|
name: "ExpetidEmailNoticeTime",
|
||||||
|
table: "VisitTask",
|
||||||
|
type: "datetime2",
|
||||||
|
nullable: true,
|
||||||
|
comment: "通知IR加急阅片时间");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "ExpetidEmailNoticeTime",
|
||||||
|
table: "VisitTask");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -13301,6 +13301,10 @@ namespace IRaCIS.Core.Infra.EFCore.Migrations
|
||||||
b.Property<Guid?>("DoctorUserId")
|
b.Property<Guid?>("DoctorUserId")
|
||||||
.HasColumnType("uniqueidentifier");
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("ExpetidEmailNoticeTime")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasComment("通知IR加急阅片时间");
|
||||||
|
|
||||||
b.Property<DateTime?>("FirstReadingTime")
|
b.Property<DateTime?>("FirstReadingTime")
|
||||||
.HasColumnType("datetime2")
|
.HasColumnType("datetime2")
|
||||||
.HasComment("首次阅片时间");
|
.HasComment("首次阅片时间");
|
||||||
|
|
Loading…
Reference in New Issue