Compare commits

...

31 Commits

Author SHA1 Message Date
hang 21136f1238 Merge branch 'Test_IRC_Net10' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net10
continuous-integration/drone/push Build is running Details
2026-08-07 14:56:37 +08:00
hang 540de91485 生成测试图像代码提交 2026-08-07 14:56:35 +08:00
he ff72c4aefc sheet页问题修改
continuous-integration/drone/push Build is running Details
2026-08-06 15:01:26 +08:00
he f94d1caa79 导入忽略空格 2026-08-06 15:01:15 +08:00
he 621fab16a0 匹配去掉空格 2026-08-06 15:01:10 +08:00
he 581ee62a5b Merge branch 'Test_IRC_Net10' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net10
continuous-integration/drone/push Build is passing Details
2026-08-06 09:19:30 +08:00
he 8d9a824ac3 提示修改 2026-08-06 09:19:21 +08:00
he 4357185ac7 导入1和0 兼容 2 2026-08-06 09:19:19 +08:00
he 10793c6a93 导入1和0 兼容 2026-08-06 09:19:17 +08:00
he 37100008eb 导出靶段备注 2026-08-06 09:19:15 +08:00
he adc9b2635a 导出模版只导出靶段 2026-08-06 09:19:12 +08:00
he ffb8894608 导出修改 2026-08-06 09:19:04 +08:00
hang 62421988c4 增加日志详细信息:get请求参数,请求 OS Browser 信息
continuous-integration/drone/push Build is passing Details
2026-08-05 14:13:42 +08:00
hang 96650ced2d Merge branch 'Test_IRC_Net10' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net10
continuous-integration/drone/push Build is running Details
2026-08-04 15:08:15 +08:00
hang cdcd3154b4 中心调研这里增加额外的验证 2026-08-04 15:08:13 +08:00
he ef7f0ebc4a 重新发布
continuous-integration/drone/push Build is running Details
2026-08-04 14:32:19 +08:00
he c5eaf847cc 修改行数 2026-08-04 14:27:46 +08:00
he 739f610d4e 字典修改
continuous-integration/drone/push Build is passing Details
2026-08-04 14:15:13 +08:00
he 5c8ccdf434 导入修改
continuous-integration/drone/push Build is running Details
2026-08-04 14:10:22 +08:00
he e0b80b3316 Merge branch 'Test_IRC_Net10' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net10
continuous-integration/drone/push Build is running Details
2026-08-04 13:32:54 +08:00
he d4bbe47b81 导出修改 2026-08-04 13:32:51 +08:00
hang dc16cc2018 Merge branch 'Test_IRC_Net10' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net10
continuous-integration/drone/push Build is running Details
2026-08-04 11:38:57 +08:00
hang 2f930ddca3 ivus隐藏导表 2026-08-04 11:38:55 +08:00
he e346593ebb Revert "拷贝病灶"
continuous-integration/drone/push Build is running Details
This reverts commit 282e56e5fc.
2026-08-03 16:47:31 +08:00
he fab6c8c55a Merge branch 'Test_IRC_Net10' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net10
continuous-integration/drone/push Build is running Details
2026-08-03 16:43:50 +08:00
he 282e56e5fc 拷贝病灶 2026-08-03 16:43:46 +08:00
hang a600f11422 Merge branch 'Test_IRC_Net10' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net10 2026-07-30 17:52:53 +08:00
hang 2db4a293ed 修复后处理增加数量bug 2026-07-30 17:52:44 +08:00
he 8641a11b53 查询修改
continuous-integration/drone/push Build is passing Details
2026-07-30 17:39:10 +08:00
he 980248d899 Merge branch 'Test_IRC_Net10' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net10
continuous-integration/drone/push Build is passing Details
2026-07-30 15:59:14 +08:00
he 372ee55241 稽查修改 2026-07-30 15:59:11 +08:00
17 changed files with 1079 additions and 42 deletions

View File

@ -4,30 +4,16 @@ using IRaCIS.Core.API.HostService;
using IRaCIS.Core.Application.BusinessFilter; using IRaCIS.Core.Application.BusinessFilter;
using IRaCIS.Core.Application.BusinessFilter.LegacyController.Database.Api; using IRaCIS.Core.Application.BusinessFilter.LegacyController.Database.Api;
using IRaCIS.Core.Application.Filter; using IRaCIS.Core.Application.Filter;
using IRaCIS.Core.Application.MassTransit.Consumer;
using IRaCIS.Core.Application.Service; using IRaCIS.Core.Application.Service;
using IRaCIS.Core.Application.Service.BusinessFilter;
using IRaCIS.Core.Infra.EFCore; using IRaCIS.Core.Infra.EFCore;
using IRaCIS.Core.Infrastructure.Extention; using IRaCIS.Core.Infrastructure.Extention;
using MassTransit; using MassTransit;
using MassTransit.NewIdProviders; using MassTransit.NewIdProviders;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Localization;
using Newtonsoft.Json; using Newtonsoft.Json;
using Serilog; using Serilog;
using System;
using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using UAParser;
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
AppContext.SetSwitch("Npgsql.DisableDateTimeInfinityConversions", true); AppContext.SetSwitch("Npgsql.DisableDateTimeInfinityConversions", true);
@ -95,6 +81,8 @@ builder.Services.AddSingleton<FileSyncQueue>();
builder.Services.AddHostedService<SyncFileRecoveryService>(); builder.Services.AddHostedService<SyncFileRecoveryService>();
builder.Services.AddHostedService<FileSyncWorker>(); builder.Services.AddHostedService<FileSyncWorker>();
builder.Services.AddSingleton(_ => Parser.GetDefault());
//minimal api 异常处理 //minimal api 异常处理
builder.Services.AddExceptionHandler<GlobalExceptionHandler>(); builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
//builder.Services.AddProblemDetails(); //builder.Services.AddProblemDetails();

View File

@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Serilog; using Serilog;
using System.Linq; using System.Linq;
using UAParser;
namespace IRaCIS.Core.API namespace IRaCIS.Core.API
{ {
@ -17,11 +18,14 @@ namespace IRaCIS.Core.API
{ {
app.UseMiddleware<RequestResponseLoggingMiddleware>(); app.UseMiddleware<RequestResponseLoggingMiddleware>();
// 从 DI 容器中获取 Parser 实例
var uaParser = app.ApplicationServices.GetRequiredService<Parser>();
app.UseSerilogRequestLogging(opts app.UseSerilogRequestLogging(opts
=> =>
{ {
opts.MessageTemplate = "{FullName} {UserType} {UserIp} {Host} {RequestMethod} {RequestPath} {RequestBody} responded {StatusCode} in {Elapsed:0.0000} ms"; opts.MessageTemplate = "{FullName} {UserType} {UserIp} {Host} {RequestMethod} {RequestPath}{QueryString} {RequestBody} {OS} {Browser} {DeviceType} responded {StatusCode} in {Elapsed:0.0000} ms";
opts.EnrichDiagnosticContext = (diagnosticContext, httpContext) => opts.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
{ {
@ -33,10 +37,9 @@ namespace IRaCIS.Core.API
// Only set it if available. You're not sending sensitive data in a querystring right?! // 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("QueryString", request.QueryString.Value);
}
diagnosticContext.Set("FullName", httpContext?.User?.FindFirst(JwtIRaCISClaimType.FullName)?.Value); diagnosticContext.Set("FullName", httpContext?.User?.FindFirst(JwtIRaCISClaimType.FullName)?.Value);
@ -52,6 +55,38 @@ namespace IRaCIS.Core.API
diagnosticContext.Set("UserIp", clientIp); diagnosticContext.Set("UserIp", clientIp);
// ==================== 新增:解析 User-Agent ====================
try
{
var userAgent = httpContext.Request.Headers["User-Agent"].ToString();
if (!string.IsNullOrEmpty(userAgent))
{
var clientInfo = uaParser.Parse(userAgent);
diagnosticContext.Set("OS", clientInfo.OS.ToString());
diagnosticContext.Set("Browser", clientInfo.UA.ToString());
//diagnosticContext.Set("DeviceType", clientInfo.Device.ToString());
diagnosticContext.Set("DeviceType", userAgent.Contains("Mobile") ? "Mobile": "Desktop");
}
else
{
diagnosticContext.Set("OS", "Unknown");
diagnosticContext.Set("Browser", "Unknown");
diagnosticContext.Set("DeviceType", "Unknown");
}
}
catch (Exception ex)
{
// 解析失败时记录异常,但不要影响主流程
diagnosticContext.Set("OS", "ParseError");
diagnosticContext.Set("Browser", "ParseError");
diagnosticContext.Set("DeviceType", "ParseError");
}
// ==============================================================
#region 非必要不记录 #region 非必要不记录
//diagnosticContext.Set("Protocol", request.Protocol); //diagnosticContext.Set("Protocol", request.Protocol);
//diagnosticContext.Set("Scheme", request.Scheme); //diagnosticContext.Set("Scheme", request.Scheme);

View File

@ -930,8 +930,8 @@ public static class ExcelExportHelper
int sheetCount = workbook.Worksheets.Count; int sheetCount = workbook.Worksheets.Count;
if (sheetCount == 2) //if (sheetCount == 2)
{ //{
if (inDto.IsEnglish) if (inDto.IsEnglish)
{ {
workbook.Worksheet(1).Delete(); // 删除第一个工作表 workbook.Worksheet(1).Delete(); // 删除第一个工作表
@ -940,7 +940,7 @@ public static class ExcelExportHelper
{ {
workbook.Worksheet(2).Delete(); // 删除第二个工作表 workbook.Worksheet(2).Delete(); // 删除第二个工作表
} }
} //}
#endregion #endregion

View File

@ -3284,6 +3284,20 @@
批次Id 批次Id
</summary> </summary>
</member> </member>
<member name="M:IRaCIS.Core.Application.Service.Inspection.InspectionService.getInspectionById(IRaCIS.Core.Application.Service.Inspection.DTO.InspectionDto)">
<summary>
根据Id获取稽查记录
</summary>
<param name="inDto"></param>
<returns></returns>
</member>
<member name="M:IRaCIS.Core.Application.Service.Inspection.InspectionService.SetJsonDetail(IRaCIS.Core.Application.Service.Inspection.DTO.InspectionDto)">
<summary>
设置稽查记录JsonDetail
</summary>
<param name="inDto"></param>
<returns></returns>
</member>
<member name="M:IRaCIS.Core.Application.Service.Inspection.InspectionService.SetTrialShowInspection(IRaCIS.Core.Application.Service.Inspection.DTO.SetTrialShowInspection)"> <member name="M:IRaCIS.Core.Application.Service.Inspection.InspectionService.SetTrialShowInspection(IRaCIS.Core.Application.Service.Inspection.DTO.SetTrialShowInspection)">
<summary> <summary>
设置项目稽查配置 设置项目稽查配置
@ -9018,6 +9032,36 @@
斑块破裂 斑块破裂
</summary> </summary>
</member> </member>
<member name="P:IRaCIS.Core.Application.Service.Reading.Dto.TemplateData.TargetSegment">
<summary>
靶段
</summary>
</member>
<member name="P:IRaCIS.Core.Application.Service.Reading.Dto.TemplateData.TargetSegmentRemarks">
<summary>
靶段备注
</summary>
</member>
<member name="P:IRaCIS.Core.Application.Service.Reading.Dto.TemplateData.ROIAllLength">
<summary>
ROI段落总长度
</summary>
</member>
<member name="P:IRaCIS.Core.Application.Service.Reading.Dto.TemplateData.PullbackFrameCount">
<summary>
回撤中的图像帧数
</summary>
</member>
<member name="P:IRaCIS.Core.Application.Service.Reading.Dto.TemplateData.PlaqueType">
<summary>
斑块类型
</summary>
</member>
<member name="P:IRaCIS.Core.Application.Service.Reading.Dto.TemplateData.AdjustReason">
<summary>
访视点备注(如有)
</summary>
</member>
<member name="P:IRaCIS.Core.Application.Service.Reading.Dto.OCTFCTUploadData.FrameNumber"> <member name="P:IRaCIS.Core.Application.Service.Reading.Dto.OCTFCTUploadData.FrameNumber">
<summary> <summary>
帧数 帧数
@ -10218,6 +10262,11 @@
问题分类 问题分类
</summary> </summary>
</member> </member>
<member name="P:IRaCIS.Core.Application.Service.Reading.Dto.GetFilterTableQuestionInDto.QuestionClassify">
<summary>
问题分类
</summary>
</member>
<member name="P:IRaCIS.Core.Application.Service.Reading.Dto.SetOncologyQuestion.EvaluationResult"> <member name="P:IRaCIS.Core.Application.Service.Reading.Dto.SetOncologyQuestion.EvaluationResult">
<summary> <summary>
评估结果 评估结果
@ -15001,6 +15050,13 @@
<param name="inDto"></param> <param name="inDto"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:IRaCIS.Core.Application.Service.ReadingImageTaskService.GetFilterTableQuestion(IRaCIS.Core.Application.Service.Reading.Dto.GetFilterTableQuestionInDto)">
<summary>
获取单个表格问题及答案 过滤问题
</summary>
<param name="inDto"></param>
<returns></returns>
</member>
<member name="M:IRaCIS.Core.Application.Service.ReadingImageTaskService.GetCustomTableQuestionAnswer(IRaCIS.Core.Application.Service.Reading.Dto.GetCustomTableQuestionAnswerInDto)"> <member name="M:IRaCIS.Core.Application.Service.ReadingImageTaskService.GetCustomTableQuestionAnswer(IRaCIS.Core.Application.Service.Reading.Dto.GetCustomTableQuestionAnswerInDto)">
<summary> <summary>
获取自定义问题以及答案 获取自定义问题以及答案
@ -16218,6 +16274,66 @@
访视数据模型 访视数据模型
</summary> </summary>
</member> </member>
<member name="P:IRaCIS.Core.Application.Service.TestService.GenerateIamgeInfo.PatientId">
<summary>
患者ID (0010,0020)
</summary>
</member>
<member name="P:IRaCIS.Core.Application.Service.TestService.GenerateIamgeInfo.PatientName">
<summary>
患者姓名 (0010,0010)
</summary>
</member>
<member name="P:IRaCIS.Core.Application.Service.TestService.GenerateIamgeInfo.StudyInstanceUID">
<summary>
研究实例UID (0020,000D)
</summary>
</member>
<member name="P:IRaCIS.Core.Application.Service.TestService.GenerateIamgeInfo.SeriesInstanceUID">
<summary>
序列实例UID (0020,000E)
</summary>
</member>
<member name="P:IRaCIS.Core.Application.Service.TestService.GenerateIamgeInfo.SOPInstanceUID">
<summary>
SOP实例UID (0008,0018)
</summary>
</member>
<member name="P:IRaCIS.Core.Application.Service.TestService.GenerateIamgeInfo.SeriesNumber">
<summary>
序列号 (0020,0011)
</summary>
</member>
<member name="P:IRaCIS.Core.Application.Service.TestService.GenerateIamgeInfo.InstanceNumber">
<summary>
实例号 (0020,0013)
</summary>
</member>
<member name="P:IRaCIS.Core.Application.Service.TestService.GenerateIamgeInfo.SeriesDescription">
<summary>
序列描述 (0008,103E)
</summary>
</member>
<member name="P:IRaCIS.Core.Application.Service.TestService.GenerateIamgeInfo.PixelSpacing">
<summary>
像素间距 (0028,0030)
</summary>
</member>
<member name="M:IRaCIS.Core.Application.Service.TestService.GenerateNoisePixel(FellowOakDicom.DicomDataset,System.String)">
<summary>
生成随机噪声图
</summary>
</member>
<member name="M:IRaCIS.Core.Application.Service.TestService.FillNoiseRectangle(System.UInt16[],System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32)">
<summary>
噪声矩形
</summary>
</member>
<member name="M:IRaCIS.Core.Application.Service.TestService.GenerateAsync(System.String,System.String,System.String,System.Int32,System.Collections.Generic.List{IRaCIS.Core.Application.Service.TestService.GenerateIamgeInfo})">
<summary>
批量生成Series
</summary>
</member>
<member name="M:IRaCIS.Core.Application.Service.TestService.MaskImage"> <member name="M:IRaCIS.Core.Application.Service.TestService.MaskImage">
<summary> <summary>
遮挡影像 遮挡影像

View File

@ -2763,11 +2763,11 @@ namespace IRaCIS.Core.Application.Service.Common
if (criterion.CriterionType == CriterionType.OCT) if (criterion.CriterionType == CriterionType.OCT)
{ {
list.Add(new ExportDocumentDes() { Code = StaticData.Export.OCT_ReadingLession_Export, ExportCatogory = ExportResult.OCT_ReadingLession_Export }); list.Add(new ExportDocumentDes() { Code = StaticData.Export.OCT_ReadingLession_Export, ExportCatogory = ExportResult.OCT_ReadingLession_Export });
list.Add(new ExportDocumentDes() { Code = StaticData.Export.OCT_CDISC_Export, ExportCatogory = ExportResult.OCT_CDISC_Export }); //list.Add(new ExportDocumentDes() { Code = StaticData.Export.OCT_CDISC_Export, ExportCatogory = ExportResult.OCT_CDISC_Export });
} }
if (criterion.CriterionType == CriterionType.IVUS) if (criterion.CriterionType == CriterionType.IVUS)
{ {
list.Add(new ExportDocumentDes() { Code = StaticData.Export.IVUS_CDISC_Export, ExportCatogory = ExportResult.IVUS_CDISC_Export }); //list.Add(new ExportDocumentDes() { Code = StaticData.Export.IVUS_CDISC_Export, ExportCatogory = ExportResult.IVUS_CDISC_Export });
} }
if (criterion.CriterionGroup != CriterionGroup.Tumor /*&& criterion.CriterionType != CriterionType.OCT && criterion.CriterionType != CriterionType.IVUS*/) if (criterion.CriterionGroup != CriterionGroup.Tumor /*&& criterion.CriterionType != CriterionType.OCT && criterion.CriterionType != CriterionType.IVUS*/)
{ {

View File

@ -708,9 +708,12 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
TaskSeries dicomSeries = await _taskSeriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId); TaskSeries dicomSeries = await _taskSeriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId);
var isNewSeries = false;
//判断重复 //判断重复
if (dicomSeries == null) if (dicomSeries == null)
{ {
isNewSeries = true;
var series = _mapper.Map<TaskSeries>(seriesItem); var series = _mapper.Map<TaskSeries>(seriesItem);
series.Id = seriesId; series.Id = seriesId;
@ -755,7 +758,10 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
await _taskInstanceRepository.AddAsync(insntance); await _taskInstanceRepository.AddAsync(insntance);
if (isNewSeries == false)
{
dicomSeries.InstanceCount++; dicomSeries.InstanceCount++;
}
findStudy.InstanceCount++; findStudy.InstanceCount++;
} }

View File

@ -449,6 +449,12 @@ namespace IRaCIS.Core.Application.Service.Inspection.DTO
public List<GetTrialShowInspectionOutDto> Children { get; set; } public List<GetTrialShowInspectionOutDto> Children { get; set; }
} }
public class InspectionDto
{
public Guid Id { get; set; }
public string? JsonDetail { get; set; }
}
public class SetTrialShowInspection public class SetTrialShowInspection
{ {

View File

@ -27,6 +27,39 @@ namespace IRaCIS.Core.Application.Service.Inspection
IRepository<FrontAuditConfig> _frontAuditConfigRepository, IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer) : BaseService, IInspectionService IRepository<FrontAuditConfig> _frontAuditConfigRepository, IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer) : BaseService, IInspectionService
{ {
/// <summary>
/// 根据Id获取稽查记录
/// </summary>
/// <param name="inDto"></param>
/// <returns></returns>
[HttpPost]
public async Task<IResponseOutput> getInspectionById(InspectionDto inDto)
{
var dataInspection = await _dataInspectionRepository.Where(x=>x.Id==inDto.Id).Select(x=> new InspectionDto()
{
Id=x.Id,
JsonDetail = x.JsonDetail,
}).FirstOrDefaultAsync();
return ResponseOutput.Ok(dataInspection);
}
/// <summary>
/// 设置稽查记录JsonDetail
/// </summary>
/// <param name="inDto"></param>
/// <returns></returns>
[HttpPost]
public async Task<IResponseOutput> SetJsonDetail(InspectionDto inDto)
{
await _dataInspectionRepository.BatchUpdateNoTrackingAsync(x => x.Id == inDto.Id, u => new DataInspection() { JsonDetail = inDto.JsonDetail });
return ResponseOutput.Ok(inDto);
}
/// <summary> /// <summary>
/// 设置项目稽查配置 /// 设置项目稽查配置
/// </summary> /// </summary>

View File

@ -250,6 +250,37 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto
public string TaskBlindName { get; set; } public string TaskBlindName { get; set; }
public string SubjectID { get; set; } public string SubjectID { get; set; }
/// <summary>
/// 靶段
/// </summary>
public string TargetSegment { get; set; } = string.Empty;
/// <summary>
/// 靶段备注
/// </summary>
public string TargetSegmentRemarks { get; set; } = string.Empty;
/// <summary>
/// ROI段落总长度
/// </summary>
public string ROIAllLength { get; set; } = string.Empty;
/// <summary>
/// 回撤中的图像帧数
/// </summary>
public string PullbackFrameCount { get; set; } = string.Empty;
/// <summary>
/// 斑块类型
/// </summary>
public string PlaqueType { get; set; } = string.Empty;
/// <summary>
/// 访视点备注(如有)
/// </summary>
public string AdjustReason { get; set; } = string.Empty;
} }
public class OCTFCTUploadData public class OCTFCTUploadData

View File

@ -1,4 +1,6 @@
using IRaCIS.Core.Application.Service.Reading.Dto; using DocumentFormat.OpenXml.Spreadsheet;
using IRaCIS.Core.Application.Interfaces;
using IRaCIS.Core.Application.Service.Reading.Dto;
using IRaCIS.Core.Application.ViewModel; using IRaCIS.Core.Application.ViewModel;
using IRaCIS.Core.Infra.EFCore.Common; using IRaCIS.Core.Infra.EFCore.Common;
using IRaCIS.Core.Infrastructure; using IRaCIS.Core.Infrastructure;
@ -6,6 +8,7 @@ using MassTransit;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
namespace IRaCIS.Core.Application.Service.ReadingCalculate namespace IRaCIS.Core.Application.Service.ReadingCalculate
{ {
@ -18,6 +21,8 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
IRepository<ReadingTableAnswerRowInfo> _readingTableAnswerRowInfoRepository, IRepository<ReadingTableAnswerRowInfo> _readingTableAnswerRowInfoRepository,
IRepository<ReadingQuestionTrial> _readingQuestionTrialRepository, IRepository<ReadingQuestionTrial> _readingQuestionTrialRepository,
IRepository<OrganInfo> _organInfoRepository, IRepository<OrganInfo> _organInfoRepository,
ISubjectVisitService iSubjectVisitService,
IRepository<Dictionary> _dictionaryRepository,
IRepository<ReadingGlobalTaskInfo> _readingGlobalTaskInfoRepository, IRepository<ReadingGlobalTaskInfo> _readingGlobalTaskInfoRepository,
IRepository<SubjectVisit> _subjectVisitRepository, IRepository<SubjectVisit> _subjectVisitRepository,
IRepository<TumorAssessment_IRECIST1Point1> _tumorAssessmentRepository, IRepository<TumorAssessment_IRECIST1Point1> _tumorAssessmentRepository,
@ -414,10 +419,70 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
var taskinfo = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).Include(x=>x.Subject).FirstNotNullAsync(); var taskinfo = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).Include(x=>x.Subject).FirstNotNullAsync();
//靶段
var targetSegmentdic = await _dictionaryRepository.Where(x => x.Parent.Code == "TargetSegment").ToListAsync();
//斑块类型
var plaqueTypedic = await _dictionaryRepository.Where(x => x.Parent.Code == "PlaqueType").ToListAsync();
var awswerList=await _readingTaskQuestionAnswerRepository.Where(x => x.VisitTaskId == inDto.VisitTaskId).Include(x=>x.ReadingQuestionTrial).ToListAsync();
var targetSegmentAnswer = awswerList.Where(x => x.ReadingQuestionTrial.QuestionType == QuestionType.TargetSegment).Select(x => x.Answer).FirstOrDefault() ?? string.Empty;
if (targetSegmentAnswer.IsNotNullOrEmpty())
{
var data= JsonConvert.DeserializeObject<List<string>>(targetSegmentAnswer);
targetSegmentAnswer = string.Join(",", targetSegmentdic
.Where(x => data.Contains(x.Code))
.Select(x => x.ValueCN));
}
else
{
var targetSegmentData = await iSubjectVisitService.GetReadingVisitStudyList(new Contracts.GetReadingVisitStudyListIndto()
{
SujectVisitId = taskinfo.SourceSubjectVisitId ?? default(Guid),
TrialId = taskinfo.TrialId,
VisitTaskId = inDto.VisitTaskId
});
targetSegmentAnswer = string.Join(",", targetSegmentData
.Where(x => x.BodyPartForEdit.IsNotNullOrEmpty())
.SelectMany(x => x.BodyPartForEdit.Split('|'))
.Where(s => !string.IsNullOrWhiteSpace(s))
.Distinct());
}
var plaqueTypeAnswer = awswerList.Where(x => x.ReadingQuestionTrial.QuestionType == QuestionType.PlaqueType).Select(x => x.Answer).FirstOrDefault() ?? string.Empty;
if (plaqueTypeAnswer.IsNotNullOrEmpty())
{
var data = JsonConvert.DeserializeObject<List<string>>(plaqueTypeAnswer);
plaqueTypeAnswer = string.Join(",", plaqueTypedic
.Where(x => data.Contains(x.Code))
.Select(x => x.ValueCN));
}
var values = new TemplateData() var values = new TemplateData()
{ {
SubjectID = (taskinfo.BlindSubjectCode.IsNullOrEmpty() ? taskinfo.Subject.Code : taskinfo.BlindSubjectCode), SubjectID = (taskinfo.BlindSubjectCode.IsNullOrEmpty() ? taskinfo.Subject.Code : taskinfo.BlindSubjectCode),
TaskBlindName = taskinfo.TaskBlindName, TaskBlindName = taskinfo.TaskBlindName,
TargetSegment= targetSegmentAnswer,
TargetSegmentRemarks = awswerList.Where(x=>x.ReadingQuestionTrial.QuestionType == QuestionType.TargetSegmentRemarks).Select(x=>x.Answer).FirstOrDefault()??string.Empty,
//ROIAllLength = awswerList.Where(x => x.ReadingQuestionTrial.QuestionType == QuestionType.ROIAllLength).Select(x => x.Answer).FirstOrDefault() ?? string.Empty,
//PullbackFrameCount = awswerList.Where(x => x.ReadingQuestionTrial.QuestionType == QuestionType.PullbackFrameCount).Select(x => x.Answer).FirstOrDefault() ?? string.Empty,
//AdjustReason = awswerList.Where(x => x.ReadingQuestionTrial.QuestionType == QuestionType.AdjustReason).Select(x => x.Answer).FirstOrDefault() ?? string.Empty,
//PlaqueType = plaqueTypeAnswer,
}; };
return await _visitTaskHelpeService.ExportTemplateAsync(new IRaCIS.Application.Contracts.ExportTemplateAsyncDto() return await _visitTaskHelpeService.ExportTemplateAsync(new IRaCIS.Application.Contracts.ExportTemplateAsyncDto()
{ {
@ -442,7 +507,7 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
List<string> sheetNames = new List<string>() List<string> sheetNames = new List<string>()
{ {
"各匹配片段测量值","MeasuredValue" "ROI测量值","ROI 测量值"
}; };
if (sheetNames.Intersect(uploadInfo.SheetNames).Count() == 0) if (sheetNames.Intersect(uploadInfo.SheetNames).Count() == 0)
@ -510,6 +575,8 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
Dictionary<string, string> isPresent = new Dictionary<string, string>() Dictionary<string, string> isPresent = new Dictionary<string, string>()
{ {
{ "1","1"},
{ "0","0"},
{ "有","1"}, { "有","1"},
{ "无","0"}, { "无","0"},
{ "Existence","1"}, { "Existence","1"},
@ -520,6 +587,8 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
Dictionary<string, string> yesOrNo = new Dictionary<string, string>() Dictionary<string, string> yesOrNo = new Dictionary<string, string>()
{ {
{ "1","1"},
{ "0","0"},
{ "是","1"}, { "是","1"},
{ "否","0"}, { "否","0"},
{ "Yes","1"}, { "Yes","1"},
@ -529,7 +598,7 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
List<IVUSMeasuredValue> measuredValueList = new List<IVUSMeasuredValue>(); List<IVUSMeasuredValue> measuredValueList = new List<IVUSMeasuredValue>();
var errorRow = new List<int> { }; var errorRow = new List<int> { };
for (int i = 3; i < dataTable.Rows.Count; i++) for (int i = 9; i < dataTable.Rows.Count; i++)
{ {
IVUSMeasuredValue iVUSMeasuredValue = new IVUSMeasuredValue() { }; IVUSMeasuredValue iVUSMeasuredValue = new IVUSMeasuredValue() { };
try try
@ -691,7 +760,100 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
} }
} }
#region 构造外层问题答案
List<ReadingTaskQuestionAnswer> questionAnswers = new List<ReadingTaskQuestionAnswer>();
var readingQuestionList = await _readingQuestionTrialRepository.Where(x => x.ReadingQuestionCriterionTrialId == taskinfo.TrialReadingCriterionId).ToListAsync();
var targetSegmentAnswer = dataTable.Rows[2]["B"].ToString();
if (targetSegmentAnswer.IsNotNullOrEmpty())
{
var targetSegmentValues = targetSegmentAnswer.Replace(" ", "").Replace("", ",").Split(",").ToList();
var targetSegmentdic = await _dictionaryRepository.Where(x => x.Parent.Code == "TargetSegment").ToListAsync();
targetSegmentAnswer = JsonConvert.SerializeObject(targetSegmentdic.Where(x => targetSegmentValues.Contains(x.ValueCN.Replace(" ", ""))).Select(x => x.Code).ToList());
}
questionAnswers.Add(new ReadingTaskQuestionAnswer()
{
Answer= targetSegmentAnswer??string.Empty,
ReadingQuestionCriterionTrialId = taskinfo.TrialReadingCriterionId,
ReadingQuestionTrialId = readingQuestionList.Where(x => x.QuestionType == QuestionType.TargetSegment).Select(x => x.Id).FirstOrDefault(),
TrialId = taskinfo.TrialId,
SubjectId= taskinfo.SubjectId,
VisitTaskId = taskinfo.Id,
});
questionAnswers.Add(new ReadingTaskQuestionAnswer()
{
Answer = dataTable.Rows[3]["B"].ToString()??string.Empty,
ReadingQuestionCriterionTrialId = taskinfo.TrialReadingCriterionId,
ReadingQuestionTrialId = readingQuestionList.Where(x => x.QuestionType == QuestionType.TargetSegmentRemarks).Select(x => x.Id).FirstOrDefault(),
TrialId = taskinfo.TrialId,
SubjectId = taskinfo.SubjectId,
VisitTaskId = taskinfo.Id,
});
questionAnswers.Add(new ReadingTaskQuestionAnswer()
{
Answer = dataTable.Rows[4]["B"].ToString() ?? string.Empty,
ReadingQuestionCriterionTrialId = taskinfo.TrialReadingCriterionId,
ReadingQuestionTrialId = readingQuestionList.Where(x => x.QuestionType == QuestionType.ROIAllLength).Select(x => x.Id).FirstOrDefault(),
TrialId = taskinfo.TrialId,
SubjectId = taskinfo.SubjectId,
VisitTaskId = taskinfo.Id,
});
questionAnswers.Add(new ReadingTaskQuestionAnswer()
{
Answer = dataTable.Rows[5]["B"].ToString() ?? string.Empty,
ReadingQuestionCriterionTrialId = taskinfo.TrialReadingCriterionId,
ReadingQuestionTrialId = readingQuestionList.Where(x => x.QuestionType == QuestionType.PullbackFrameCount).Select(x => x.Id).FirstOrDefault(),
TrialId = taskinfo.TrialId,
SubjectId = taskinfo.SubjectId,
VisitTaskId = taskinfo.Id,
});
var plaqueTypeAnswer = dataTable.Rows[6]["B"].ToString();
if (plaqueTypeAnswer.IsNotNullOrEmpty())
{
var plaqueTypeAnswerValues = plaqueTypeAnswer.Replace(" ", "").Replace("", ",").Split(",").ToList();
var plaqueTypeAnswerdic = await _dictionaryRepository.Where(x => x.Parent.Code == "PlaqueType").ToListAsync();
plaqueTypeAnswer = JsonConvert.SerializeObject(plaqueTypeAnswerdic
.Where(x => plaqueTypeAnswerValues.Any(val => x.ValueCN.Replace(" ", "").Contains(val)))
.Select(x => x.Code)
.ToList());
}
questionAnswers.Add(new ReadingTaskQuestionAnswer()
{
Answer = plaqueTypeAnswer ?? string.Empty,
ReadingQuestionCriterionTrialId = taskinfo.TrialReadingCriterionId,
ReadingQuestionTrialId = readingQuestionList.Where(x => x.QuestionType == QuestionType.PlaqueType).Select(x => x.Id).FirstOrDefault(),
TrialId = taskinfo.TrialId,
SubjectId = taskinfo.SubjectId,
VisitTaskId = taskinfo.Id,
});
questionAnswers.Add(new ReadingTaskQuestionAnswer()
{
Answer = dataTable.Rows[7]["B"].ToString() ?? string.Empty,
ReadingQuestionCriterionTrialId = taskinfo.TrialReadingCriterionId,
ReadingQuestionTrialId = readingQuestionList.Where(x => x.QuestionType == QuestionType.AdjustReason).Select(x => x.Id).FirstOrDefault(),
TrialId = taskinfo.TrialId,
SubjectId = taskinfo.SubjectId,
VisitTaskId = taskinfo.Id,
});
var questionids = questionAnswers.Select(x => x.ReadingQuestionTrialId).ToList();
await _readingTaskQuestionAnswerRepository.DeleteFromQueryAsync(x => x.VisitTaskId == taskinfo.Id && questionids.Contains(x.ReadingQuestionTrialId));
await _readingTableQuestionAnswerRepository.SaveChangesAsync();
await _readingTaskQuestionAnswerRepository.AddRangeAsync(questionAnswers);
await _readingTaskQuestionAnswerRepository.SaveChangesAsync();
#endregion
await _readingTableAnswerRowInfoRepository.DeleteFromQueryAsync(x => x.QuestionId == questionInfo.Id && x.VisitTaskId == taskinfo.Id); await _readingTableAnswerRowInfoRepository.DeleteFromQueryAsync(x => x.QuestionId == questionInfo.Id && x.VisitTaskId == taskinfo.Id);
await _readingTableQuestionAnswerRepository.DeleteFromQueryAsync(x => x.QuestionId == questionInfo.Id && x.VisitTaskId == taskinfo.Id); await _readingTableQuestionAnswerRepository.DeleteFromQueryAsync(x => x.QuestionId == questionInfo.Id && x.VisitTaskId == taskinfo.Id);
await _readingTableQuestionAnswerRepository.SaveChangesAsync(); await _readingTableQuestionAnswerRepository.SaveChangesAsync();

View File

@ -2,6 +2,7 @@
using DocumentFormat.OpenXml.Drawing.Spreadsheet; using DocumentFormat.OpenXml.Drawing.Spreadsheet;
using DocumentFormat.OpenXml.Office.SpreadSheetML.Y2023.MsForms; using DocumentFormat.OpenXml.Office.SpreadSheetML.Y2023.MsForms;
using DocumentFormat.OpenXml.Office2010.Excel; using DocumentFormat.OpenXml.Office2010.Excel;
using IRaCIS.Core.Application.Interfaces;
using IRaCIS.Core.Application.Service.Reading.Dto; using IRaCIS.Core.Application.Service.Reading.Dto;
using IRaCIS.Core.Application.ViewModel; using IRaCIS.Core.Application.ViewModel;
using IRaCIS.Core.Domain.Models; using IRaCIS.Core.Domain.Models;
@ -12,6 +13,7 @@ using MassTransit;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
namespace IRaCIS.Core.Application.Service.ReadingCalculate namespace IRaCIS.Core.Application.Service.ReadingCalculate
{ {
@ -24,6 +26,8 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
IRepository<ReadingTableAnswerRowInfo> _readingTableAnswerRowInfoRepository, IRepository<ReadingTableAnswerRowInfo> _readingTableAnswerRowInfoRepository,
IRepository<ReadingQuestionTrial> _readingQuestionTrialRepository, IRepository<ReadingQuestionTrial> _readingQuestionTrialRepository,
IRepository<OrganInfo> _organInfoRepository, IRepository<OrganInfo> _organInfoRepository,
ISubjectVisitService iSubjectVisitService,
IRepository<Dictionary> _dictionaryRepository,
IHttpContextAccessor httpContext, IHttpContextAccessor httpContext,
IRepository<ReadingGlobalTaskInfo> _readingGlobalTaskInfoRepository, IRepository<ReadingGlobalTaskInfo> _readingGlobalTaskInfoRepository,
IRepository<SubjectVisit> _subjectVisitRepository, IRepository<SubjectVisit> _subjectVisitRepository,
@ -271,10 +275,68 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
public async Task<FileResult> GetOCTFCTTemplate(GetExportTemplateInDto inDto) public async Task<FileResult> GetOCTFCTTemplate(GetExportTemplateInDto inDto)
{ {
var taskinfo = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).Include(x => x.Subject).FirstNotNullAsync(); var taskinfo = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).Include(x => x.Subject).FirstNotNullAsync();
//靶段
var targetSegmentdic = await _dictionaryRepository.Where(x => x.Parent.Code == "TargetSegment").ToListAsync();
//斑块类型
var plaqueTypedic = await _dictionaryRepository.Where(x => x.Parent.Code == "PlaqueType").ToListAsync();
var awswerList = await _readingTaskQuestionAnswerRepository.Where(x => x.VisitTaskId == inDto.VisitTaskId).Include(x => x.ReadingQuestionTrial).ToListAsync();
var targetSegmentAnswer = awswerList.Where(x => x.ReadingQuestionTrial.QuestionType == QuestionType.TargetSegment).Select(x => x.Answer).FirstOrDefault() ?? string.Empty;
if (targetSegmentAnswer.IsNotNullOrEmpty())
{
var data = JsonConvert.DeserializeObject<List<string>>(targetSegmentAnswer);
targetSegmentAnswer = string.Join(",", targetSegmentdic
.Where(x => data.Contains(x.Code))
.Select(x => x.ValueCN));
}
else
{
var targetSegmentData = await iSubjectVisitService.GetReadingVisitStudyList(new Contracts.GetReadingVisitStudyListIndto()
{
SujectVisitId = taskinfo.SourceSubjectVisitId ?? default(Guid),
TrialId = taskinfo.TrialId,
VisitTaskId = inDto.VisitTaskId
});
targetSegmentAnswer = string.Join(",", targetSegmentData
.Where(x => x.BodyPartForEdit.IsNotNullOrEmpty())
.SelectMany(x => x.BodyPartForEdit.Split('|'))
.Where(s => !string.IsNullOrWhiteSpace(s))
.Distinct());
}
var plaqueTypeAnswer = awswerList.Where(x => x.ReadingQuestionTrial.QuestionType == QuestionType.PlaqueType).Select(x => x.Answer).FirstOrDefault() ?? string.Empty;
if (plaqueTypeAnswer.IsNotNullOrEmpty())
{
var data = JsonConvert.DeserializeObject<List<string>>(plaqueTypeAnswer);
plaqueTypeAnswer = string.Join(",", plaqueTypedic
.Where(x => data.Contains(x.Code))
.Select(x => x.ValueCN));
}
var values = new TemplateData() var values = new TemplateData()
{ {
SubjectID = (taskinfo.BlindSubjectCode.IsNullOrEmpty() ? taskinfo.Subject.Code : taskinfo.BlindSubjectCode), SubjectID = (taskinfo.BlindSubjectCode.IsNullOrEmpty() ? taskinfo.Subject.Code : taskinfo.BlindSubjectCode),
TaskBlindName = taskinfo.TaskBlindName, TaskBlindName = taskinfo.TaskBlindName,
TargetSegment = targetSegmentAnswer,
TargetSegmentRemarks = awswerList.Where(x => x.ReadingQuestionTrial.QuestionType == QuestionType.TargetSegmentRemarks).Select(x => x.Answer).FirstOrDefault() ?? string.Empty,
//ROIAllLength = awswerList.Where(x => x.ReadingQuestionTrial.QuestionType == QuestionType.ROIAllLength).Select(x => x.Answer).FirstOrDefault() ?? string.Empty,
////PullbackFrameCount = awswerList.Where(x => x.ReadingQuestionTrial.QuestionType == QuestionType.PullbackFrameCount).Select(x => x.Answer).FirstOrDefault() ?? string.Empty,
//PlaqueType = plaqueTypeAnswer,
//AdjustReason = awswerList.Where(x => x.ReadingQuestionTrial.QuestionType == QuestionType.AdjustReason).Select(x => x.Answer).FirstOrDefault() ?? string.Empty,
}; };
return await _visitTaskHelpeService.ExportTemplateAsync(new IRaCIS.Application.Contracts.ExportTemplateAsyncDto() return await _visitTaskHelpeService.ExportTemplateAsync(new IRaCIS.Application.Contracts.ExportTemplateAsyncDto()
{ {
@ -299,7 +361,7 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
List<string> sheetNames = new List<string>() List<string> sheetNames = new List<string>()
{ {
"FCT导入","FCT" "ROI测量值","ROI 测量值"
}; };
if (sheetNames.Intersect(uploadInfo.SheetNames).Count() == 0) if (sheetNames.Intersect(uploadInfo.SheetNames).Count() == 0)
@ -338,6 +400,8 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
Dictionary<string, string> yesOrNo = new Dictionary<string, string>() Dictionary<string, string> yesOrNo = new Dictionary<string, string>()
{ {
{ "1","1"},
{ "0","0"},
{ "是","1"}, { "是","1"},
{ "否","0"}, { "否","0"},
{ "Yes","1"}, { "Yes","1"},
@ -356,11 +420,12 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
return value; return value;
} }
} }
for (int i = 3; i < dataTable.Rows.Count; i++) for (int i = 8; i < dataTable.Rows.Count; i++)
{ {
try try
{ {
// 手动抛出异常进入catch // 手动抛出异常进入catch
if (!yesOrNo.ContainsKey(dataTable.Rows[i]["K"].ToString())|| !yesOrNo.ContainsKey(dataTable.Rows[i]["L"].ToString())) if (!yesOrNo.ContainsKey(dataTable.Rows[i]["K"].ToString())|| !yesOrNo.ContainsKey(dataTable.Rows[i]["L"].ToString()))
{ {
@ -404,6 +469,8 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
Dictionary<string, string> isPresent = new Dictionary<string, string>() Dictionary<string, string> isPresent = new Dictionary<string, string>()
{ {
{ "1","1"},
{ "0","0"},
{ "有","1"}, { "有","1"},
{ "无","0"}, { "无","0"},
{ "Existence","1"}, { "Existence","1"},
@ -411,7 +478,7 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
{ string.Empty,string.Empty}, { string.Empty,string.Empty},
}; };
int row = 3; int row = 8;
foreach (var item in measuredValueList) foreach (var item in measuredValueList)
{ {
row++; row++;
@ -664,6 +731,91 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate
} }
} }
#region 构造外层问题答案
List<ReadingTaskQuestionAnswer> questionAnswers = new List<ReadingTaskQuestionAnswer>();
var readingQuestionList = await _readingQuestionTrialRepository.Where(x => x.ReadingQuestionCriterionTrialId == taskinfo.TrialReadingCriterionId).ToListAsync();
var targetSegmentAnswer = dataTable.Rows[2]["B"].ToString();
if (targetSegmentAnswer.IsNotNullOrEmpty())
{
var targetSegmentValues = targetSegmentAnswer.Replace(" ", "").Replace("", ",").Split(",").ToList();
var targetSegmentdic = await _dictionaryRepository.Where(x => x.Parent.Code == "TargetSegment").ToListAsync();
targetSegmentAnswer = JsonConvert.SerializeObject(targetSegmentdic.Where(x => targetSegmentValues.Contains(x.ValueCN.Replace(" ", ""))).Select(x => x.Code).ToList());
}
questionAnswers.Add(new ReadingTaskQuestionAnswer()
{
Answer = targetSegmentAnswer ?? string.Empty,
ReadingQuestionCriterionTrialId = taskinfo.TrialReadingCriterionId,
ReadingQuestionTrialId = readingQuestionList.Where(x => x.QuestionType == QuestionType.TargetSegment).Select(x => x.Id).FirstOrDefault(),
TrialId = taskinfo.TrialId,
SubjectId = taskinfo.SubjectId,
VisitTaskId = taskinfo.Id,
});
questionAnswers.Add(new ReadingTaskQuestionAnswer()
{
Answer = dataTable.Rows[3]["B"].ToString() ?? string.Empty,
ReadingQuestionCriterionTrialId = taskinfo.TrialReadingCriterionId,
ReadingQuestionTrialId = readingQuestionList.Where(x => x.QuestionType == QuestionType.TargetSegmentRemarks).Select(x => x.Id).FirstOrDefault(),
TrialId = taskinfo.TrialId,
SubjectId = taskinfo.SubjectId,
VisitTaskId = taskinfo.Id,
});
questionAnswers.Add(new ReadingTaskQuestionAnswer()
{
Answer = dataTable.Rows[4]["B"].ToString() ?? string.Empty,
ReadingQuestionCriterionTrialId = taskinfo.TrialReadingCriterionId,
ReadingQuestionTrialId = readingQuestionList.Where(x => x.QuestionType == QuestionType.ROIAllLength).Select(x => x.Id).FirstOrDefault(),
TrialId = taskinfo.TrialId,
SubjectId = taskinfo.SubjectId,
VisitTaskId = taskinfo.Id,
});
var plaqueTypeAnswer = dataTable.Rows[5]["B"].ToString();
if (plaqueTypeAnswer.IsNotNullOrEmpty())
{
var plaqueTypeAnswerValues = plaqueTypeAnswer.Replace(" ", "").Replace("", ",").Split(",").ToList();
var plaqueTypeAnswerdic = await _dictionaryRepository.Where(x => x.Parent.Code == "PlaqueType").ToListAsync();
plaqueTypeAnswer = JsonConvert.SerializeObject(plaqueTypeAnswerdic
.Where(x => plaqueTypeAnswerValues.Any(val => x.ValueCN.Replace(" ", "").Contains(val)))
.Select(x => x.Code)
.ToList());
}
questionAnswers.Add(new ReadingTaskQuestionAnswer()
{
Answer = plaqueTypeAnswer ?? string.Empty,
ReadingQuestionCriterionTrialId = taskinfo.TrialReadingCriterionId,
ReadingQuestionTrialId = readingQuestionList.Where(x => x.QuestionType == QuestionType.PlaqueType).Select(x => x.Id).FirstOrDefault(),
TrialId = taskinfo.TrialId,
SubjectId = taskinfo.SubjectId,
VisitTaskId = taskinfo.Id,
});
questionAnswers.Add(new ReadingTaskQuestionAnswer()
{
Answer = dataTable.Rows[6]["B"].ToString() ?? string.Empty,
ReadingQuestionCriterionTrialId = taskinfo.TrialReadingCriterionId,
ReadingQuestionTrialId = readingQuestionList.Where(x => x.QuestionType == QuestionType.AdjustReason).Select(x => x.Id).FirstOrDefault(),
TrialId = taskinfo.TrialId,
SubjectId = taskinfo.SubjectId,
VisitTaskId = taskinfo.Id,
});
var questionids = questionAnswers.Select(x => x.ReadingQuestionTrialId).ToList();
await _readingTaskQuestionAnswerRepository.DeleteFromQueryAsync(x => x.VisitTaskId == taskinfo.Id && questionids.Contains(x.ReadingQuestionTrialId));
await _readingTableQuestionAnswerRepository.SaveChangesAsync();
await _readingTaskQuestionAnswerRepository.AddRangeAsync(questionAnswers);
await _readingTaskQuestionAnswerRepository.SaveChangesAsync();
#endregion
await _readingTableAnswerRowInfoRepository.DeleteFromQueryAsync(x => x.QuestionId == questionInfo.Id && x.VisitTaskId == taskinfo.Id); await _readingTableAnswerRowInfoRepository.DeleteFromQueryAsync(x => x.QuestionId == questionInfo.Id && x.VisitTaskId == taskinfo.Id);
await _readingTableQuestionAnswerRepository.DeleteFromQueryAsync(x => x.QuestionId == questionInfo.Id && x.VisitTaskId == taskinfo.Id); await _readingTableQuestionAnswerRepository.DeleteFromQueryAsync(x => x.QuestionId == questionInfo.Id && x.VisitTaskId == taskinfo.Id);
await _readingTableQuestionAnswerRepository.SaveChangesAsync(); await _readingTableQuestionAnswerRepository.SaveChangesAsync();

View File

@ -56,6 +56,12 @@ namespace IRaCIS.Core.Application.Contracts
var item = addOrEditTrialSiteUserSurvey; var item = addOrEditTrialSiteUserSurvey;
if (item.UserTypeId == null)
{
throw new BusinessValidationFailedException(_localizer["SystemCommon_WebErrorArgument"]);
}
//找下系统中是否存在该用户类型的 并且邮箱 或者手机的账户 //找下系统中是否存在该用户类型的 并且邮箱 或者手机的账户
var sysUserInfo = await _identityUserRepository.Where(t => t.EMail == item.Email,true).Include(t => t.UserRoleList).FirstOrDefaultAsync(); var sysUserInfo = await _identityUserRepository.Where(t => t.EMail == item.Email,true).Include(t => t.UserRoleList).FirstOrDefaultAsync();

View File

@ -2,6 +2,7 @@
using ClosedXML.Excel; using ClosedXML.Excel;
using FellowOakDicom; using FellowOakDicom;
using FellowOakDicom.Imaging; using FellowOakDicom.Imaging;
using FellowOakDicom.IO.Buffer;
using IRaCIS.Application.Contracts; using IRaCIS.Application.Contracts;
using IRaCIS.Core.Application.Contracts; using IRaCIS.Core.Application.Contracts;
using IRaCIS.Core.Application.Helper; using IRaCIS.Core.Application.Helper;
@ -57,6 +58,9 @@ namespace IRaCIS.Core.Application.Service
{ {
public static int IntValue = 100; public static int IntValue = 100;
private readonly Random random = new();
static string ReplaceText(string text, Dictionary<string, string> replaceRules) static string ReplaceText(string text, Dictionary<string, string> replaceRules)
{ {
foreach (var rule in replaceRules) foreach (var rule in replaceRules)
@ -431,6 +435,439 @@ namespace IRaCIS.Core.Application.Service
} }
public async Task<IResponseOutput> GenerateImage()
{
var sourceDir = @"C:\work\测量图像";
var targetDir = @"C:\work\测量图像\纯黑色";
Directory.CreateDirectory(targetDir);
foreach (var inputPath in Directory.EnumerateFiles(sourceDir, "*", SearchOption.TopDirectoryOnly))
{
var relativePath = Path.GetRelativePath(sourceDir, inputPath);
var outputPath = Path.Combine(targetDir, relativePath);
var outputFolder = Path.GetDirectoryName(outputPath);
if (!string.IsNullOrEmpty(outputFolder))
{
Directory.CreateDirectory(outputFolder);
}
await using var input = File.OpenRead(inputPath);
await using var output = File.Create(outputPath);
try
{
Console.WriteLine($"开始处理: {relativePath}");
var dicomFile = await DicomFile.OpenAsync(input, FileReadOption.ReadAll).ConfigureAwait(false);
var ds = dicomFile.Dataset;
var rows = ds.GetSingleValue<int>(DicomTag.Rows);
var cols = ds.GetSingleValue<int>(DicomTag.Columns);
var bitsAllocated = ds.GetSingleValue<int>(DicomTag.BitsAllocated);
var bitsStored = ds.GetSingleValueOrDefault(DicomTag.BitsStored, bitsAllocated);
var samplesPerPixel = ds.GetSingleValue<int>(DicomTag.SamplesPerPixel);
var pixelRepresentation = ds.GetSingleValueOrDefault(DicomTag.PixelRepresentation, (ushort)0);
//Console.WriteLine($"Working Rows={rows}, Cols={cols}, BitsAllocated={bitsAllocated}, BitsStored={bitsStored}, SamplesPerPixel={samplesPerPixel}, PixelRepresentation={pixelRepresentation}");
//遮盖成黑色
var regions = new[] { new MaskRegion(0, 0, rows, cols) };
await DicomPixelMasker.MaskAsync(input, output, regions, new DicomMaskOptions() { GrayscaleMaskValue = 0 });
}
catch (Exception ex)
{
// 跳过该文件
Console.WriteLine();
Console.WriteLine($"error: {relativePath} —————— {ex.Message}");
Console.WriteLine();
}
Console.WriteLine($"处理结束: {relativePath}");
}
var list = new List<GenerateIamgeInfo>();
await GenerateAsync(@"C:\work\测量图像\纯黑色\CT.dcm", @"C:\work\测量图像\output", "CT", 20, list);
await GenerateAsync(@"C:\work\测量图像\纯黑色\MR.dcm", @"C:\work\测量图像\output", "MR", 20, list);
await GenerateAsync(@"C:\work\测量图像\纯黑色\PT.dcm", @"C:\work\测量图像\output", "PT", 10, list);
string exportPath = @$"C:\work\测量图像\outPut.xlsx";
MiniExcel.SaveAs(exportPath, list,overwriteFile:true);
return ResponseOutput.Ok();
}
#region 噪声
public class GenerateIamgeInfo
{
/// <summary>
/// 患者ID (0010,0020)
/// </summary>
public string PatientId { get; set; }
/// <summary>
/// 患者姓名 (0010,0010)
/// </summary>
public string PatientName { get; set; }
/// <summary>
/// 研究实例UID (0020,000D)
/// </summary>
public string StudyInstanceUID { get; set; }
/// <summary>
/// 序列实例UID (0020,000E)
/// </summary>
public string SeriesInstanceUID { get; set; }
/// <summary>
/// SOP实例UID (0008,0018)
/// </summary>
public string SOPInstanceUID { get; set; }
/// <summary>
/// 序列号 (0020,0011)
/// </summary>
public int SeriesNumber { get; set; }
/// <summary>
/// 实例号 (0020,0013)
/// </summary>
public int InstanceNumber { get; set; }
/// <summary>
/// 序列描述 (0008,103E)
/// </summary>
public string SeriesDescription { get; set; }
public int WidthMM => Width / 2;
public int HeightMM => Height / 2;
public int X { get; set; }
public int Y { get; set; }
public int Width { get; set; }
public int Height { get; set; }
/// <summary>
/// 像素间距 (0028,0030)
/// </summary>
public string PixelSpacing { get; set; }
}
/// <summary>
/// 生成随机噪声图
/// </summary>
private MaskRegion GenerateNoisePixel(
DicomDataset ds,
string modality)
{
Random random = new Random(); // 创建实例
int rows = ds.GetSingleValue<int>(DicomTag.Rows);
int cols = ds.GetSingleValue<int>(DicomTag.Columns);
int bits = ds.GetSingleValue<int>(DicomTag.BitsAllocated);
if (bits != 16)
{
throw new Exception(
"只处理16bit CT/MR/PT");
}
ushort[] pixels = new ushort[rows * cols];
//
// 黑底
//
for (int i = 0; i < pixels.Length; i++)
{
pixels[i] = 0;
}
// PixelSpacing
double pixelSpacing = 0.5;
// 生成10cm-20cm矩形
double widthMm = random.Next(100, 200);
double heightMm = random.Next(100, 200);
int width = (int)(widthMm / pixelSpacing);
int height = (int)(heightMm / pixelSpacing);
int x = random.Next(0, cols - width);
int y = random.Next(0, rows - height);
int min;
int max;
if (modality == "PT")
{
min = 0;
max = 16000;
}
else if (modality == "CT")
{
min = 5;
max = 2000;
}
else
{
// MR
min = 5;
max = 2000;
}
FillNoiseRectangle(pixels, cols, x, y, width, height, min, max);
//
// ushort转byte
//
byte[] bytes = new byte[pixels.Length * 2];
Buffer.BlockCopy(pixels, 0, bytes, 0, bytes.Length);
ds.Remove(DicomTag.PixelData);
//ds.Add(DicomTag.PixelData,new DicomOtherByte(DicomTag.PixelData, bytes));
var pixelData = DicomPixelData.Create(ds, true);
pixelData.AddFrame(new MemoryByteBuffer(bytes));
return new MaskRegion(x, y, width, height);
}
/// <summary>
/// 噪声矩形
/// </summary>
private void FillNoiseRectangle(
ushort[] pixels,
int cols,
int x,
int y,
int width,
int height,
int min,
int max)
{
Random random = new Random(); // 创建实例
for (int row = y;
row < y + height;
row++)
{
for (int col = x;
col < x + width;
col++)
{
int index =
row * cols + col;
//
// 每个像素不同
//
pixels[index] = (ushort)random.Next(min, max);
}
}
}
/// <summary>
/// 批量生成Series
/// </summary>
public async Task GenerateAsync(
string template,
string outputDir,
string modality,
int count, List<GenerateIamgeInfo> generateIamgeInfos)
{
Directory.CreateDirectory(outputDir);
//
// Series UID
//
string seriesUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID;
//
// SeriesNumber
//
int seriesNumber = random.Next(1, 101);
for (int i = 1; i <= count; i++)
{
var file = await DicomFile.OpenAsync(template);
var ds = file.Dataset;
//
// Series信息
//
ds.AddOrUpdate(DicomTag.SeriesInstanceUID, seriesUid);
ds.AddOrUpdate(DicomTag.SeriesNumber, seriesNumber);
ds.AddOrUpdate(DicomTag.InstanceNumber, i);
if (modality == "CT")
{
ds.AddOrUpdate(DicomTag.SliceLocation, (i * 5).ToString());
ds.AddOrUpdate(DicomTag.RescaleSlope, "1");
ds.AddOrUpdate(DicomTag.RescaleIntercept, "-1024");
}
if (modality == "PT")
{
// 生成随机身高 (1.60 - 1.80 米)
Random rand = new Random();
double height = Math.Round(rand.NextDouble() * 0.20 + 1.60, 2);
// 根据身高生成符合BMI的体重 (BMI范围 18.5 - 24.9)
double bmi = Math.Round(rand.NextDouble() * 6.4 + 18.5, 1);
double weight = Math.Round(bmi * height * height, 1);
ds.AddOrUpdate(DicomTag.PatientSize, height.ToString("F2"));
ds.AddOrUpdate(DicomTag.PatientWeight, weight.ToString("F1"));
}
var seriesDescription = $"{modality} Data";
ds.AddOrUpdate(DicomTag.SeriesDescription, seriesDescription);
var sopUid = DicomUIDGenerator.GenerateDerivedFromUUID().UID;
//
// SOP Instance UID
//
ds.AddOrUpdate(DicomTag.SOPInstanceUID, sopUid);
//
// Modality
//
ds.AddOrUpdate(DicomTag.Modality, modality);
// ============================
// 2. 修改像素间距
// ============================
ds.AddOrUpdate(DicomTag.PixelSpacing, "0.5\\0.5");
//
// 生成麻子PixelData
//
var region = GenerateNoisePixel(ds, modality);
//
// 文件名
//
string fileName = Path.Combine(outputDir, $"{modality}_{i:D3}.dcm");
await file.SaveAsync(fileName);
var studyUid = ds.GetSingleValueOrDefault(DicomTag.StudyInstanceUID, string.Empty);
var pid = ds.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty);
var pName = ds.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty);
generateIamgeInfos.Add(new GenerateIamgeInfo()
{
// 初始化默认值
PatientId = pid,
PatientName = pName,
StudyInstanceUID = studyUid,
SeriesInstanceUID = seriesUid,
SOPInstanceUID = sopUid,
SeriesDescription = seriesDescription,
SeriesNumber = seriesNumber,
InstanceNumber = i,
X = region.X,
Y = region.Y,
Width = region.Width,
Height = region.Height,
PixelSpacing = "0.5\\0.5",
});
}
}
#endregion
/// <summary> /// <summary>
/// 遮挡影像 /// 遮挡影像
/// </summary> /// </summary>
@ -1728,10 +2165,10 @@ namespace IRaCIS.Core.Application.Service
//var d = _dicRepository.UpdateFromDTOAsync(new AddOrEditBasicDic() { Id = Guid.Parse("60d86683-c33b-4349-b672-08da1e91b622"), ParentId = null, ChildGroup = null, Code = null }, true, true).Result; //var d = _dicRepository.UpdateFromDTOAsync(new AddOrEditBasicDic() { Id = Guid.Parse("60d86683-c33b-4349-b672-08da1e91b622"), ParentId = null, ChildGroup = null, Code = null }, true, true).Result;
//var a = 123; //var a = 123;
throw new BusinessValidationFailedException(_localizer["TrialIsDoubleCheck{0}", "测试"]); //throw new BusinessValidationFailedException(_localizer["TrialIsDoubleCheck{0}", "测试"]);
var b = _localizer["test{0}", "测试"]; //var b = _localizer["test{0}", "测试"];
return _localizer["test{0}", "测试"]; //return _localizer["test{0}", "测试"];
//var list = _subjectVisitRepository.Where(t => t.Id == Guid.NewGuid()).SelectMany(t => t.VisitTaskList).ToList(); //var list = _subjectVisitRepository.Where(t => t.Id == Guid.NewGuid()).SelectMany(t => t.VisitTaskList).ToList();
@ -1741,6 +2178,8 @@ namespace IRaCIS.Core.Application.Service
//var list3 = _visitTaskRepository.Where(t => t.Id == Guid.NewGuid()).SelectMany(t => t.SourceSubjectVisit.VisitTaskList).ToList(); //var list3 = _visitTaskRepository.Where(t => t.Id == Guid.NewGuid()).SelectMany(t => t.SourceSubjectVisit.VisitTaskList).ToList();
//var list2 = _subjectRepository.Where(t => t.Id == Guid.NewGuid()).SelectMany(t => t.SubjectVisitTaskList).ToList(); //var list2 = _subjectRepository.Where(t => t.Id == Guid.NewGuid()).SelectMany(t => t.SubjectVisitTaskList).ToList();
//return _userInfo.LocalIp; //return _userInfo.LocalIp;
return _userInfo.ClientInfo;
} }

View File

@ -3060,6 +3060,11 @@ namespace IRaCIS.Core.Domain.Share
/// </summary> /// </summary>
ROIAllLength = 1015, ROIAllLength = 1015,
/// <summary>
/// 斑块类型
/// </summary>
PlaqueType=1018,
/// <summary> /// <summary>
/// PAV冠状动脉粥样硬化体积百分比) /// PAV冠状动脉粥样硬化体积百分比)
/// </summary> /// </summary>

View File

@ -63,5 +63,17 @@ namespace IRaCIS.Core.Domain.Share
string BrowserFingerprint { get; } string BrowserFingerprint { get; }
UserWorkLanguage UserWorkLanguage { get; } UserWorkLanguage UserWorkLanguage { get; }
ClientInfo ClientInfo { get; }
} }
public class ClientInfo
{
public string Browser { get; set; }
public string OS { get; set; }
public string DeviceType { get; set; }
} }
}

View File

@ -1,19 +1,15 @@
using IRaCIS.Core.Domain.Models; using IRaCIS.Core.Domain.Models;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using UAParser;
namespace IRaCIS.Core.Domain.Share namespace IRaCIS.Core.Domain.Share
{ {
/// <summary> /// <summary>
/// 用户信息 /// 用户信息
/// </summary> /// </summary>
public class UserInfo : IUserInfo public class UserInfo(IHttpContextAccessor _accessor, Parser parser) : IUserInfo
{ {
private readonly IHttpContextAccessor _accessor;
public UserInfo(IHttpContextAccessor accessor)
{
_accessor = accessor;
}
/// <summary> /// <summary>
/// 用户角色Id /// 用户角色Id
@ -348,6 +344,55 @@ namespace IRaCIS.Core.Domain.Share
} }
public ClientInfo ClientInfo
{
get
{
try
{
var userAgent = _accessor?.HttpContext.Request.Headers["User-Agent"].ToString();
if (!string.IsNullOrEmpty(userAgent))
{
var clientInfo = parser.Parse(userAgent);
return new ClientInfo()
{
OS = clientInfo.OS.ToString(),
Browser = clientInfo.UA.ToString(),
DeviceType = userAgent.Contains("Mobile") ? "Mobile" : "Desktop"
};
}
else
{
return new ClientInfo()
{
OS = "Unknown",
Browser = "Unknown",
DeviceType = "Unknown"
};
}
}
catch (Exception ex)
{
return new ClientInfo()
{
OS = "ParseError",
Browser = "ParseError",
DeviceType = "ParseError"
};
}
}
}
public Guid? SignId public Guid? SignId
{ {

View File

@ -15,6 +15,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="UAParser" Version="3.1.47" />
<PackageReference Include="EFCore.BulkExtensions" Version="10.0.1" /> <PackageReference Include="EFCore.BulkExtensions" Version="10.0.1" />
<PackageReference Include="EntityFrameworkCore.Triggered" Version="3.2.2" /> <PackageReference Include="EntityFrameworkCore.Triggered" Version="3.2.2" />
<PackageReference Include="EntityFrameworkCore.Projectables" Version="6.0.5" /> <PackageReference Include="EntityFrameworkCore.Projectables" Version="6.0.5" />