中心调研修改,oss测试
parent
1a6ec6b315
commit
339414694d
|
|
@ -16,6 +16,7 @@ using Minio;
|
|||
using Minio.DataModel;
|
||||
using Minio.DataModel.Args;
|
||||
using Minio.Exceptions;
|
||||
using System.IO;
|
||||
using System.Reactive.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Web;
|
||||
|
|
@ -142,6 +143,11 @@ public enum ObjectStoreUse
|
|||
|
||||
public interface IOSSService
|
||||
{
|
||||
public void SetImmediateArchiveRule(string prefix,
|
||||
StorageClass targetStorageClass,
|
||||
string ruleId = "immediate-archive");
|
||||
|
||||
|
||||
public Task<string> UploadToOSSAsync(Stream fileStream, string oosFolderPath, string fileRealName, bool isFileNameAddGuid = true);
|
||||
public Task<string> UploadToOSSAsync(string localFilePath, string oosFolderPath, bool isFileNameAddGuid = true, bool randomFileName = false);
|
||||
|
||||
|
|
@ -182,7 +188,185 @@ public class OSSService : IOSSService
|
|||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将指定前缀下的所有现有文件立即转为目标存储类型
|
||||
/// 核心:Days = 0 表示对所有存量文件立即生效
|
||||
/// </summary>
|
||||
/// <param name="prefix">要转换的文件前缀,如 "project-a/logs/"</param>
|
||||
/// <param name="targetStorageClass">目标存储类型</param>
|
||||
/// <param name="ruleId">规则ID,默认为"immediate-archive"</param>
|
||||
public void SetImmediateArchiveRule(string prefix,
|
||||
StorageClass targetStorageClass,
|
||||
string ruleId = "immediate-archive")
|
||||
{
|
||||
|
||||
BackBatchGetToken();
|
||||
|
||||
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||
|
||||
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
|
||||
|
||||
try
|
||||
{
|
||||
// 1. 先获取现有的所有生命周期规则(避免覆盖)
|
||||
var existingRules = new List<Aliyun.OSS.LifecycleRule>();
|
||||
try
|
||||
{
|
||||
var existingRuleList = _ossClient.GetBucketLifecycle(aliConfig.BucketName);
|
||||
if (existingRuleList != null)
|
||||
{
|
||||
existingRules.AddRange(existingRuleList);
|
||||
Console.WriteLine($"找到 {existingRules.Count} 条现有规则");
|
||||
}
|
||||
}
|
||||
catch (OssException ex) when (ex.ErrorCode == "NoSuchLifecycle")
|
||||
{
|
||||
// 如果没有生命周期规则,继续创建新规则
|
||||
Console.WriteLine("当前Bucket无生命周期规则,将创建新规则");
|
||||
}
|
||||
|
||||
// 2. 创建立即生效的转换规则
|
||||
|
||||
ruleId = $"{ruleId}_{prefix}";
|
||||
var immediateRule = new Aliyun.OSS.LifecycleRule
|
||||
{
|
||||
ID = ruleId,
|
||||
Prefix = prefix,
|
||||
Status = RuleStatus.Enabled,
|
||||
Transitions = new Aliyun.OSS.LifecycleRule.LifeCycleTransition[]
|
||||
{
|
||||
new Aliyun.OSS.LifecycleRule.LifeCycleTransition
|
||||
{
|
||||
|
||||
// 关键设置:Days = 0 表示对最后修改时间≤当前的所有文件生效
|
||||
LifeCycleExpiration =
|
||||
{
|
||||
Days = 1
|
||||
},
|
||||
StorageClass = targetStorageClass
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// 3. 移除同名的旧规则(如果存在)
|
||||
existingRules.RemoveAll(r => r.ID == ruleId);
|
||||
|
||||
// 4. 添加新规则到规则列表
|
||||
existingRules.Add(immediateRule);
|
||||
|
||||
|
||||
var request = new SetBucketLifecycleRequest(aliConfig.BucketName)
|
||||
{
|
||||
LifecycleRules= existingRules
|
||||
};
|
||||
|
||||
|
||||
_ossClient.SetBucketLifecycle(request);
|
||||
|
||||
Console.WriteLine("✅ 立即归档规则设置成功!");
|
||||
Console.WriteLine($" 规则ID: {ruleId}");
|
||||
Console.WriteLine($" 前缀: {prefix}");
|
||||
Console.WriteLine($" 目标存储类型: {targetStorageClass}");
|
||||
Console.WriteLine($" 生效时间: 将在下次生命周期扫描时生效(通常24小时内)");
|
||||
}
|
||||
catch (OssException ex)
|
||||
{
|
||||
Console.WriteLine($"❌ 设置失败 [错误码: {ex.ErrorCode}]");
|
||||
Console.WriteLine($" 详细: {ex.Message}");
|
||||
|
||||
// 处理特定错误
|
||||
if (ex.ErrorCode == "InvalidArgument")
|
||||
{
|
||||
Console.WriteLine(" 可能原因:存储类型不支持或参数格式错误");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"❌ 发生未知错误: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//public async Task SetLifecycle(string lifecycle)
|
||||
//{
|
||||
|
||||
|
||||
// if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||
// {
|
||||
// var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||
|
||||
// var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
|
||||
|
||||
|
||||
// var rule = new Aliyun.OSS.LifecycleRule
|
||||
// {
|
||||
// ID = "ArchiveOldFiles",
|
||||
// Prefix = "", // 全 bucket 生效
|
||||
// Status = RuleStatus.Enabled
|
||||
// };
|
||||
|
||||
// // 30 天转低频
|
||||
// rule.Transitions.Add(new Aliyun.OSS.LifecycleRule.LifeCycleTransition
|
||||
// {
|
||||
// Days = 30,
|
||||
// StorageClass = StorageClass.IA
|
||||
// });
|
||||
|
||||
// // 180 天转归档
|
||||
// rule.Transitions.Add(new Aliyun.OSS.LifecycleRule.LifeCycleTransition
|
||||
// {
|
||||
// Days = 180,
|
||||
// StorageClass = StorageClass.Archive
|
||||
// });
|
||||
|
||||
// // 365 天转冷归档
|
||||
// rule.Transitions.Add(new Aliyun.OSS.LifecycleRule.LifeCycleTransition
|
||||
// {
|
||||
// Days = 365,
|
||||
// StorageClass = StorageClass.ColdArchive
|
||||
// });
|
||||
|
||||
// // 730 天转深度归档
|
||||
// rule.Transitions.Add(new Aliyun.OSS.LifecycleRule.LifeCycleTransition
|
||||
// {
|
||||
// Days = 730,
|
||||
// StorageClass = StorageClass.DeepColdArchive
|
||||
// });
|
||||
|
||||
// var request = new SetBucketLifecycleRequest(aliConfig.BucketName);
|
||||
// request.AddLifecycleRule(rule);
|
||||
|
||||
// _ossClient.SetBucketLifecycle(request);
|
||||
|
||||
|
||||
// }
|
||||
// else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||
// {
|
||||
// var awsConfig = ObjectStoreServiceOptions.AWS;
|
||||
|
||||
// var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
|
||||
|
||||
|
||||
|
||||
// //提供awsEndPoint(域名)进行访问配置
|
||||
// var clientConfig = new AmazonS3Config
|
||||
// {
|
||||
// RegionEndpoint = RegionEndpoint.USEast1,
|
||||
// UseHttp = true,
|
||||
// };
|
||||
|
||||
// var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||
|
||||
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||
// }
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// oosFolderPath 不要 "/ "开头 应该: TempFolder/ChildFolder
|
||||
|
|
|
|||
|
|
@ -15538,6 +15538,13 @@
|
|||
<param name="outEnrollTime"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.Application.Service.TestService.TestTrialEfficacyEvaluationStat(System.Collections.Generic.List{IRaCIS.Core.Application.Service.TestService.TestEfficacyEvaluation})">
|
||||
<summary>
|
||||
测试疗效评估
|
||||
</summary>
|
||||
<param name="list"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.Application.Service.TestService.RebuildAuditDocumentClosureAsync(IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.AuditDocumentClosure},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.AuditDocument})">
|
||||
<summary>
|
||||
重建闭包表
|
||||
|
|
@ -15705,6 +15712,15 @@
|
|||
利用DocX 库 处理word国际化模板
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.Application.Helper.OSSService.SetImmediateArchiveRule(System.String,Aliyun.OSS.StorageClass,System.String)">
|
||||
<summary>
|
||||
将指定前缀下的所有现有文件立即转为目标存储类型
|
||||
核心:Days = 0 表示对所有存量文件立即生效
|
||||
</summary>
|
||||
<param name="prefix">要转换的文件前缀,如 "project-a/logs/"</param>
|
||||
<param name="targetStorageClass">目标存储类型</param>
|
||||
<param name="ruleId">规则ID,默认为"immediate-archive"</param>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.Application.Helper.OSSService.UploadToOSSAsync(System.IO.Stream,System.String,System.String,System.Boolean)">
|
||||
<summary>
|
||||
oosFolderPath 不要 "/ "开头 应该: TempFolder/ChildFolder
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ namespace IRaCIS.Core.Application.Service
|
|||
public interface IMailVerificationService
|
||||
{
|
||||
|
||||
Task AnolymousSendEmail(string researchProgramNo, string emailAddress, int verificationCode);
|
||||
Task AnolymousSendEmail(Guid trialId, string emailAddress, int verificationCode);
|
||||
|
||||
Task SendEmailVerification(string emailAddress, int verificationCode);
|
||||
|
||||
|
|
@ -276,10 +276,13 @@ namespace IRaCIS.Core.Application.Service
|
|||
|
||||
|
||||
//中心调研 登陆 发送验证码
|
||||
public async Task AnolymousSendEmail(string researchProgramNo, string emailAddress, int verificationCode)
|
||||
public async Task AnolymousSendEmail(Guid trialId, string emailAddress, int verificationCode)
|
||||
{
|
||||
//throw new BusinessValidationFailedException("模拟邮件取数据或者发送异常!!!");
|
||||
|
||||
|
||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialId);
|
||||
|
||||
var messageToSend = new MimeMessage();
|
||||
//发件地址
|
||||
messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail));
|
||||
|
|
@ -292,7 +295,7 @@ namespace IRaCIS.Core.Application.Service
|
|||
|
||||
Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input =>
|
||||
{
|
||||
var topicStr = string.Format(input.topicStr, companyName, researchProgramNo);
|
||||
var topicStr = string.Format(input.topicStr, companyName, trialInfo.ResearchProgramNo);
|
||||
|
||||
var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr),
|
||||
|
||||
|
|
|
|||
|
|
@ -287,9 +287,8 @@ namespace IRaCIS.Core.Application.Contracts
|
|||
//验证码 6位
|
||||
int verificationCode = new Random().Next(100000, 1000000);
|
||||
|
||||
var trialInfo = await _trialRepository.FirstOrDefaultAsync(t => t.Id == userInfo.TrialId);
|
||||
|
||||
await SafeMailHelper.Run(async () => await _mailVerificationService.AnolymousSendEmail(trialInfo.ResearchProgramNo, userInfo.Email, verificationCode));
|
||||
await SafeMailHelper.Run(async () => await _mailVerificationService.AnolymousSendEmail(userInfo.TrialId, userInfo.Email, verificationCode));
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -197,13 +197,14 @@ public class TrialStatService(
|
|||
var exceptVisit = list.Where(t => t.ReadingCategory == ReadingCategory.Visit)
|
||||
.GroupBy(t => new { t.SubjectCode, t.TaskName }).Where(g => g.Count() == 1).Select(g => new { g.Key.SubjectCode, g.Key.TaskName }).ToList();
|
||||
|
||||
//将一个人做的访视任务过滤掉
|
||||
list = list.Where(t => !exceptVisit.Any(ev => ev.SubjectCode == t.SubjectCode && ev.TaskName == t.TaskName)).ToList();
|
||||
}
|
||||
|
||||
|
||||
list = list.OrderBy(t => t.SubjectCode).ThenBy(t => t.ArmEnum).ThenBy(t => t.VisitTaskNum).ToList();
|
||||
|
||||
//处理裁判标记
|
||||
//处理裁判标记 是否触发裁判,裁判选择标记
|
||||
list = DealJudgeMark(criterion.ArbitrationRule, criterion.IsGlobalReading, list);
|
||||
|
||||
//基线(最晚拍片日期)-首次PD(所有触发PD的病灶的检查的最早拍片日期)的中位数,单位是天
|
||||
|
|
@ -215,7 +216,7 @@ public class TrialStatService(
|
|||
g => g.First().LatestScanDate // 如果同一个 SubjectCode 有多条记录,只取第一条
|
||||
);
|
||||
|
||||
//一定要两个人做完了,产生了裁判并且有结果的,否则就不算,并且排除基线
|
||||
//找到裁判选择了的,否则就不算,并且排除基线 按照每个subject分组,取最后一次访视
|
||||
list = list.Where(t => t.IsJudgeSelect == true && t.VisitTaskNum != 0 && t.ReadingCategory==ReadingCategory.Visit)//全局的答案已经放在对应访视上了
|
||||
.GroupBy(t => t.SubjectCode).Select(g => g.OrderByDescending(t => t.VisitTaskNum).First()).ToList();
|
||||
|
||||
|
|
|
|||
|
|
@ -75,6 +75,77 @@ namespace IRaCIS.Core.Application.Service
|
|||
{
|
||||
public static int IntValue = 100;
|
||||
|
||||
[AllowAnonymous]
|
||||
public async Task<IResponseOutput> TestOSS()
|
||||
{
|
||||
_IOSSService.SetImmediateArchiveRule("Test-Archive", StorageClass.Archive);
|
||||
|
||||
return ResponseOutput.Ok();
|
||||
}
|
||||
|
||||
|
||||
public class TestEfficacyEvaluation
|
||||
{
|
||||
public string SubjectCode { get; set; }
|
||||
|
||||
public string TaskName { get; set; }
|
||||
|
||||
public decimal VisitTaskNum { get; set; }
|
||||
|
||||
public string Arm { get; set; }
|
||||
|
||||
public bool? IsJudgeSelect { get; set; }
|
||||
|
||||
public bool? IsTrigerJudge { get; set; }
|
||||
|
||||
public string OverallTumorEvaluation { get; set; }
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async Task<IResponseOutput> TestUploadTrialEfficacyEvaluation(IFormFile file)
|
||||
{
|
||||
using var stream = file.OpenReadStream();
|
||||
|
||||
var list = MiniExcel
|
||||
.Query<TestEfficacyEvaluation>(stream, excelType: ExcelType.XLSX)
|
||||
.ToList();
|
||||
|
||||
return await TestTrialEfficacyEvaluationStat(list);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 测试疗效评估
|
||||
/// </summary>
|
||||
/// <param name="list"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<IResponseOutput> TestTrialEfficacyEvaluationStat(List<TestEfficacyEvaluation> list)
|
||||
{
|
||||
|
||||
//找到只有一个人阅片的受试者 和访视
|
||||
var exceptVisit = list
|
||||
.GroupBy(t => new { t.SubjectCode, t.TaskName }).Where(g => g.Count() == 1).Select(g => new { g.Key.SubjectCode, g.Key.TaskName }).ToList();
|
||||
|
||||
//将一个人做的访视任务过滤掉
|
||||
list = list.Where(t => !exceptVisit.Any(ev => ev.SubjectCode == t.SubjectCode && ev.TaskName == t.TaskName)).ToList();
|
||||
|
||||
|
||||
//找到裁判选择了的,否则就不算,并且排除基线 按照每个subject分组,取最后一次访视
|
||||
list = list.Where(t => t.IsJudgeSelect == true && t.VisitTaskNum != 0)//全局的答案已经放在对应访视上了
|
||||
.GroupBy(t => t.SubjectCode).Select(g => g.OrderByDescending(t => t.VisitTaskNum).First()).ToList();
|
||||
|
||||
|
||||
var result = list.GroupBy(t => t.OverallTumorEvaluation).Select(t => new
|
||||
{
|
||||
OverallTumorEvaluation = t.Key,
|
||||
SubjectList = t.Select(t => t.SubjectCode).ToList()
|
||||
}).ToList();
|
||||
|
||||
return ResponseOutput.Ok(result);
|
||||
}
|
||||
|
||||
|
||||
[AllowAnonymous]
|
||||
public async Task<IResponseOutput> DealIVUSOCTDicomTag(string subjectCode, bool? isUploadOss)
|
||||
{
|
||||
|
|
@ -102,7 +173,7 @@ namespace IRaCIS.Core.Application.Service
|
|||
{
|
||||
Console.WriteLine(item.Path);
|
||||
|
||||
await using var stream = await _IOSSService.GetStreamFromOSSAsync(item.Path);
|
||||
await using var stream = await _IOSSService.GetStreamFromOSSAsync(item.Path);
|
||||
|
||||
var dicomFile = DicomFile.Open(stream, FileReadOption.ReadLargeOnDemand);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue