From 6e363601a6d6edb729afa01c64a3ec07058319aa Mon Sep 17 00:00:00 2001 From: hang <872297557@qq.com> Date: Mon, 1 Sep 2025 14:27:00 +0800 Subject: [PATCH 1/4] =?UTF-8?q?GA=20=E9=9D=9E=E7=AE=A1=E7=90=86=E5=91=98?= =?UTF-8?q?=E7=9C=8B=E5=88=B0=E7=9A=84=E4=BA=BA=E5=91=98=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IRaCIS.Core.Application/Service/Management/UserService.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/IRaCIS.Core.Application/Service/Management/UserService.cs b/IRaCIS.Core.Application/Service/Management/UserService.cs index cb816f4b3..9707e4013 100644 --- a/IRaCIS.Core.Application/Service/Management/UserService.cs +++ b/IRaCIS.Core.Application/Service/Management/UserService.cs @@ -461,6 +461,8 @@ namespace IRaCIS.Core.Application.Service public async Task> GetUserList(UserListQueryDTO inQuery) { + var isGAGroupAdmin = _userInfo.HospitalGroupAdminIdList.Any(); + var userQueryable = _identityUserRepository.Where(x => x.UserRoleList.Any(x => x.UserTypeEnum != UserTypeEnum.SuperAdmin)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.UserName), t => t.UserName.Contains(inQuery.UserName)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.RealName), t => t.FullName.Contains(inQuery.RealName)) @@ -482,8 +484,8 @@ namespace IRaCIS.Core.Application.Service .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.OA, t => !t.UserRoleList.Any(t => t.UserTypeEnum == UserTypeEnum.Admin || t.UserTypeEnum == UserTypeEnum.SuperAdmin)) //GA 只能看有该课题组的用户,并且不是admin oa - .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.GA && _userInfo.HospitalGroupAdminIdList.Any(), t => !t.UserRoleList.Any(t => t.UserTypeEnum == UserTypeEnum.Admin || t.UserTypeEnum == UserTypeEnum.SuperAdmin || t.UserTypeEnum == UserTypeEnum.OA) - && t.IdentityUserHospitalGroupList.Any(t => _userInfo.HospitalGroupAdminIdList.Contains(t.HospitalGroupId))) + .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.GA, t => !t.UserRoleList.Any(t => t.UserTypeEnum == UserTypeEnum.Admin || t.UserTypeEnum == UserTypeEnum.SuperAdmin || t.UserTypeEnum == UserTypeEnum.OA) + && t.IdentityUserHospitalGroupList.Any(t => isGAGroupAdmin ? _userInfo.HospitalGroupAdminIdList.Contains(t.HospitalGroupId) : false)) .ProjectTo(_mapper.ConfigurationProvider); return await userQueryable.ToPagedListAsync(inQuery); From ed5f8e8d9c6b8c24fd12c7d6aba97b268ca297e3 Mon Sep 17 00:00:00 2001 From: hang <872297557@qq.com> Date: Mon, 1 Sep 2025 14:55:35 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E8=87=AA=E5=8A=A8=E7=BB=91=E5=AE=9A?= =?UTF-8?q?=E9=80=BB=E8=BE=91bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IRC.Core.SCP/Service/PatientStudyService.cs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/IRC.Core.SCP/Service/PatientStudyService.cs b/IRC.Core.SCP/Service/PatientStudyService.cs index f41366145..a809fd90c 100644 --- a/IRC.Core.SCP/Service/PatientStudyService.cs +++ b/IRC.Core.SCP/Service/PatientStudyService.cs @@ -78,7 +78,26 @@ namespace IRaCIS.Core.SCP.Service //3、 未提交的最小的访视号 从这个访视开始绑定 - var subjectMaxVisitNum = startBindVisitNum == null ? subjectAllVisitList.Where(t => t.SubmitState != SubmitStateEnum.Submitted).MinOrDefault(t => t.VisitNum) : startBindVisitNum.Value; + decimal subjectMaxVisitNum = 0; + + if (startBindVisitNum == null) + { + if (subjectAllVisitList.Any(t => t.SubmitState != SubmitStateEnum.Submitted)) + { + subjectMaxVisitNum = subjectAllVisitList.Where(t => t.SubmitState != SubmitStateEnum.Submitted).MinOrDefault(t => t.VisitNum); + } + else + { + //没有未提交的,那么开始绑定的就是已提交的加1 + subjectMaxVisitNum = subjectAllVisitList.Last().VisitNum + 1; + } + } + else + { + subjectMaxVisitNum = startBindVisitNum.Value; + } + + //var subjectMaxVisitNum = startBindVisitNum == null ? subjectAllVisitList.Where(t => t.SubmitState != SubmitStateEnum.Submitted).MinOrDefault(t => t.VisitNum) : startBindVisitNum.Value; List<(int VisitCount, Guid SCPStudyId)> visits = new List<(int, Guid)>(); From 6f8f6000ba55403f22e7939943cb4e08b718b27b Mon Sep 17 00:00:00 2001 From: hang <872297557@qq.com> Date: Mon, 1 Sep 2025 15:12:23 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BD=B1=E5=83=8F?= =?UTF-8?q?=E5=88=86=E4=BA=AB=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IRaCIS.Core.API/appsettings.Test_HIR.json | 5 +++-- .../BusinessFilter/_Config/_AppSettings.cs | 4 ++++ .../Service/ImageAndDoc/ImageShareService.cs | 4 +++- IRaCIS.Core.Application/Service/Management/UserService.cs | 6 +++++- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/IRaCIS.Core.API/appsettings.Test_HIR.json b/IRaCIS.Core.API/appsettings.Test_HIR.json index 0f52c6d19..093dc9938 100644 --- a/IRaCIS.Core.API/appsettings.Test_HIR.json +++ b/IRaCIS.Core.API/appsettings.Test_HIR.json @@ -21,7 +21,7 @@ //"viewEndpoint": "https://hir.test.extimaging.com/oss/hir-test" "ViewEndpoint": "https://hir-oss.test.extimaging.com/hir-test" } - + }, "ConnectionStrings": { "RemoteNew": "Server=106.14.89.110,1435;Database=Test_HIR_New;User ID=sa;Password=xc@123456;TrustServerCertificate=true", @@ -41,7 +41,8 @@ // 是否强制用户定期修改密码 "IsNeedChangePassWord": true, // 密码有效期(天),到期后必须修改 - "ChangePassWordDays": 1000 + "ChangePassWordDays": 1000, + "OpenImageShare": true, }, "SystemEmailSendConfig": { diff --git a/IRaCIS.Core.Application/BusinessFilter/_Config/_AppSettings.cs b/IRaCIS.Core.Application/BusinessFilter/_Config/_AppSettings.cs index 465bf5d6e..702d3fc24 100644 --- a/IRaCIS.Core.Application/BusinessFilter/_Config/_AppSettings.cs +++ b/IRaCIS.Core.Application/BusinessFilter/_Config/_AppSettings.cs @@ -43,6 +43,8 @@ public class ServiceVerifyConfigOption public int CmoveInstanceIntervalMinutes { get; set; } + + public bool OpenImageShare { get; set; } } public class SystemEmailSendConfig @@ -92,6 +94,8 @@ public class SystemEmailSendConfigView public string CompanyShortNameCN { get; set; } = string.Empty; public string EmailRegexStr { get; set; } + + public bool OpenImageShare { get; set; } } public class SystemPacsConfig diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/ImageShareService.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/ImageShareService.cs index 398546f4c..1c9a46264 100644 --- a/IRaCIS.Core.Application/Service/ImageAndDoc/ImageShareService.cs +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/ImageShareService.cs @@ -5,6 +5,7 @@ using IRaCIS.Core.Application.Contracts.Dicom.DTO; using IRaCIS.Core.Domain.Share; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using System.Windows.Input; namespace IRaCIS.Core.Application.Services { @@ -27,6 +28,7 @@ namespace IRaCIS.Core.Application.Services var addEntity = await _imageShareRepository.AddAsync(imageShare); + addEntity.ExpireTime = DateTime.Now.AddDays(7); //验证码 4位 int verificationPassWord = new Random().Next(1000, 10000); @@ -36,7 +38,7 @@ namespace IRaCIS.Core.Application.Services var success = await _imageShareRepository.SaveChangesAsync(); - return ResponseOutput.Result(success, new { ResourceId = imageShare.Id, Password = verificationPassWord.ToString() }); + return ResponseOutput.Result(success, new { ResourceId = imageShare.Id, Password = verificationPassWord.ToString(), ImageShareExpireDays = 7 }); } diff --git a/IRaCIS.Core.Application/Service/Management/UserService.cs b/IRaCIS.Core.Application/Service/Management/UserService.cs index 9707e4013..3b041089c 100644 --- a/IRaCIS.Core.Application/Service/Management/UserService.cs +++ b/IRaCIS.Core.Application/Service/Management/UserService.cs @@ -1125,7 +1125,8 @@ namespace IRaCIS.Core.Application.Service [HttpPost] public async Task> GetUserLoginRoleList(IRCLoginDto loginDto, [FromServices] ITokenService _tokenService, - [FromServices] IOptionsMonitor _emailConfig + [FromServices] IOptionsMonitor _emailConfig, + [FromServices] IOptionsMonitor _basicSystemConfigConfig ) { @@ -1133,7 +1134,10 @@ namespace IRaCIS.Core.Application.Service var password = loginDto.Password; var emailConfig = _emailConfig.CurrentValue; + var basicConfig = _basicSystemConfigConfig.CurrentValue; var companyInfo = new SystemEmailSendConfigView() { CompanyName = emailConfig.CompanyName, CompanyNameCN = emailConfig.CompanyNameCN, CompanyShortName = emailConfig.CompanyShortName, CompanyShortNameCN = emailConfig.CompanyShortNameCN, SystemShortName = emailConfig.SystemShortName, EmailRegexStr = emailConfig.EmailRegexStr }; + companyInfo.OpenImageShare = basicConfig.OpenImageShare; + int maxFailures = _verifyConfig.CurrentValue.LoginMaxFailCount; From b869693d63f1d72b1970116248c95dffdf362eb8 Mon Sep 17 00:00:00 2001 From: hang <872297557@qq.com> Date: Mon, 1 Sep 2025 16:06:42 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=81=97=E6=BC=8F?= =?UTF-8?q?=E9=A2=86=E5=8F=96=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IRaCIS.Core.Application.xml | 12 ++- .../SubjectCriterionClaimCancelConsumer.cs | 38 +++++++ .../Reading/Dto/ReadingImageTaskViewModel.cs | 13 +++ .../ReadingImageTaskService.cs | 100 +++++++++++++++++- .../SubjectCriterionClaimCancelEvent.cs | 23 ++++ 5 files changed, 180 insertions(+), 6 deletions(-) create mode 100644 IRaCIS.Core.Application/MassTransit/Consumer/SubjectCriterionClaimCancelConsumer.cs create mode 100644 IRaCIS.Core.Domain/_DomainEvent/SubjectCriterionClaimCancelEvent.cs diff --git a/IRaCIS.Core.Application/IRaCIS.Core.Application.xml b/IRaCIS.Core.Application/IRaCIS.Core.Application.xml index 512b1297b..71bf4b20b 100644 --- a/IRaCIS.Core.Application/IRaCIS.Core.Application.xml +++ b/IRaCIS.Core.Application/IRaCIS.Core.Application.xml @@ -2154,7 +2154,7 @@ - + 账号验证,获取账号角色信息 获取临时token @@ -12648,7 +12648,7 @@ - + IR影像阅片 @@ -12993,6 +12993,14 @@ + + + 前端 仅仅释放的时候使用,领取是后端调用该方法 + + + + + 重置阅片时间 登录和解锁调用 diff --git a/IRaCIS.Core.Application/MassTransit/Consumer/SubjectCriterionClaimCancelConsumer.cs b/IRaCIS.Core.Application/MassTransit/Consumer/SubjectCriterionClaimCancelConsumer.cs new file mode 100644 index 000000000..c284c21dc --- /dev/null +++ b/IRaCIS.Core.Application/MassTransit/Consumer/SubjectCriterionClaimCancelConsumer.cs @@ -0,0 +1,38 @@ +using IRaCIS.Core.Domain; +using IRaCIS.Core.Domain._DomainEvent; +using IRaCIS.Core.Domain.Models; +using MassTransit; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Application.MassTransit.Consumer +{ + public class SubjectCriterionClaimCancelConsumer(IRepository _visitTaskRepository) : IConsumer + { + public async Task Consume(ConsumeContext context) + { + + Log.Logger.Warning($"定时任务取消领取!{context.Message.ToJsonStr()}"); + bool isInOrder = context.Message.IsInOrder; + + if (isInOrder) + { + Guid subjectId = (Guid)context.Message.SubjectId; + + Guid trialReadingCriterionId = (Guid)context.Message.TrialReadingCriterionId; + + await _visitTaskRepository.BatchUpdateNoTrackingAsync(t => t.SubjectId == subjectId && t.TrialReadingCriterionId == trialReadingCriterionId && t.ReadingTaskState != ReadingTaskState.HaveSigned, u => new VisitTask() { SubjectCriterionClaimUserId = null }); + + } + else + { + Guid id = (Guid)context.Message.VisitTaskId; + + await _visitTaskRepository.UpdatePartialFromQueryAsync(t => t.Id == id && t.ReadingTaskState != ReadingTaskState.HaveSigned, u => new VisitTask() { DoctorUserId = null }, true); + } + } + } +} diff --git a/IRaCIS.Core.Application/Service/Reading/Dto/ReadingImageTaskViewModel.cs b/IRaCIS.Core.Application/Service/Reading/Dto/ReadingImageTaskViewModel.cs index 586df651b..93efa7aff 100644 --- a/IRaCIS.Core.Application/Service/Reading/Dto/ReadingImageTaskViewModel.cs +++ b/IRaCIS.Core.Application/Service/Reading/Dto/ReadingImageTaskViewModel.cs @@ -3074,4 +3074,17 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto //{ //} + + public class ClaimSubjectDto + { + public bool IsInOrder { get; set; } + + public Guid? VisitTaskId { get; set; } + + public Guid TrialReadingCriterionId { get; set; } + + public Guid SubejctId { get; set; } + + public bool IsClaim { get; set; } + } } diff --git a/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingImageTaskService.cs b/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingImageTaskService.cs index 0f4927f11..140e8df80 100644 --- a/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingImageTaskService.cs +++ b/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingImageTaskService.cs @@ -12,13 +12,18 @@ using IRaCIS.Core.Infra.EFCore.Common; using IRaCIS.Core.Infrastructure; using MassTransit; using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.Extensions.Options; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NPOI.POIFS.Properties; using NPOI.SS.Formula.Functions; using Panda.DynamicWebApi.Attributes; +using System.Reactive.Concurrency; using ZiggyCreatures.Caching.Fusion; +using IRaCIS.Core.Domain.BaseModel; +using IRaCIS.Core.Domain._DomainEvent; namespace IRaCIS.Core.Application.Service { @@ -30,6 +35,7 @@ namespace IRaCIS.Core.Application.Service IRepository _noneDicomStudyRepository, IRepository _visitTaskRepository, IRepository _trialRepository, + IMessageScheduler _scheduler, IRepository _readingTableQuestionAnswerRepository, IRepository _readingOncologyTaskInfoRepository, IVisitTaskHelpeService _visitTaskHelpeService, @@ -72,9 +78,9 @@ namespace IRaCIS.Core.Application.Service [HttpPost] public async Task GetTNMValue(TNMValueDto inDto) { - var result= await _visitTaskRepository.Where(x=>x.Id==inDto.VisitTaskId).Select(x => new TNMValueDto() + var result = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).Select(x => new TNMValueDto() { - VisitTaskId=inDto.VisitTaskId, + VisitTaskId = inDto.VisitTaskId, TNMValue = x.TNMValue }).FirstOrDefaultAsync(); return result; @@ -93,7 +99,7 @@ namespace IRaCIS.Core.Application.Service await _visitTaskRepository.UpdatePartialFromQueryAsync(x => x.Id == inDto.VisitTaskId, x => new VisitTask() { TNMValue = inDto.TNMValue - },true); + }, true); return inDto; } @@ -771,6 +777,11 @@ namespace IRaCIS.Core.Application.Service } + if (await _visitTaskRepository.AnyAsync(x => x.Id == visitTaskid && x.DoctorUserId != _userInfo.UserRoleId && x.SubjectCriterionClaimUserId != _userInfo.UserRoleId)) + { + throw new BusinessValidationFailedException(_localizer["其他人已领取该任务,不允许操作"]); + } + if (await _visitTaskRepository.AnyAsync(x => x.Id == visitTaskid && x.TaskState != TaskState.Effect)) { throw new BusinessValidationFailedException(_localizer["ReadingImage_Beinvalid"]); @@ -2858,7 +2869,11 @@ namespace IRaCIS.Core.Application.Service TrialReadingCriterionId = x.TrialReadingCriterionId, }).FirstOrDefault(); - + if (task != null) + { + // 有序 自动领取该Subject + await ClaimOrCancelSubjectAsync(new ClaimSubjectDto() { IsClaim = true, SubejctId = task.SubjectId, TrialReadingCriterionId = task.TrialReadingCriterionId, VisitTaskId = task.VisitTaskId, IsInOrder = true }); + } } else if (inDto.SubjectId != null && trialReadingCriterion.IsReadingTaskViewInOrder == ReadingOrder.SubjectRandom) @@ -2985,6 +3000,12 @@ namespace IRaCIS.Core.Application.Service TrialReadingCriterionId = x.TrialReadingCriterionId, }).Skip(skipcount).FirstOrDefaultAsync(); + if (task != null) + { + // 无序 + await ClaimOrCancelSubjectAsync(new ClaimSubjectDto() { IsClaim = true, VisitTaskId = task.VisitTaskId, IsInOrder = false }); + } + } if (task == null) @@ -3172,6 +3193,77 @@ namespace IRaCIS.Core.Application.Service return true; } + + + /// + /// 前端 仅仅释放的时候使用,领取是后端调用该方法 + /// + /// + /// + /// + [HttpPost] + public async Task ClaimOrCancelSubjectAsync(ClaimSubjectDto claimSubjectDto) + { + + + //有序 用SubjectCriterionClaimUserId 某个标准的所有任务,该字段保持一致 + if (claimSubjectDto.IsInOrder) + { + + if (claimSubjectDto.IsClaim) + { + //每个subject 每个标准的领取放在 任务上 免得加表 + if (_visitTaskRepository.Any(t => t.Id == claimSubjectDto.SubejctId && t.TrialReadingCriterionId == claimSubjectDto.TrialReadingCriterionId + && t.SubjectCriterionClaimUserId != _userInfo.UserRoleId && t.SubjectCriterionClaimUserId != null)) + { + throw new BusinessValidationFailedException(_localizer["ReadingImageTask_TaskTaken"]); + } + else + { + await _visitTaskRepository.BatchUpdateNoTrackingAsync(t => t.SubjectId == claimSubjectDto.SubejctId && t.TrialReadingCriterionId == claimSubjectDto.TrialReadingCriterionId, u => new VisitTask() { SubjectCriterionClaimUserId = _userInfo.UserRoleId }); + + //列表可以看到当前阅片人是谁 + await _visitTaskRepository.BatchUpdateNoTrackingAsync(t => t.Id == claimSubjectDto.VisitTaskId, u => new VisitTask() { DoctorUserId = _userInfo.UserRoleId }); + } + + + var domainEvent = new SubjectCriterionClaimCancelEvent() { SubjectId = claimSubjectDto.SubejctId, TrialReadingCriterionId = claimSubjectDto.TrialReadingCriterionId, IsInOrder = claimSubjectDto.IsInOrder }; + await _scheduler.SchedulePublish(DateTime.Now.AddDays(1), (object)domainEvent); + + + //BackgroundJob.Schedule(t => t.CancelQCObtaion(subjectVisitId, DateTime.Now), TimeSpan.FromHours(1)); + + } + else + { + await _visitTaskRepository.BatchUpdateNoTrackingAsync(t => t.SubjectId == claimSubjectDto.SubejctId && t.TrialReadingCriterionId == claimSubjectDto.TrialReadingCriterionId && t.ReadingTaskState != ReadingTaskState.HaveSigned, u => new VisitTask() { SubjectCriterionClaimUserId = null }); + } + } + //无序 直接用DoctorUserId 当做领取人 + else + { + if (claimSubjectDto.IsClaim) + { + if (_visitTaskRepository.Any(t => t.Id == claimSubjectDto.VisitTaskId && t.DoctorUserId != _userInfo.UserRoleId && t.DoctorUserId != null)) + { + throw new BusinessValidationFailedException(_localizer["ReadingImageTask_TaskTaken"]); + } + else + { + await _visitTaskRepository.UpdatePartialFromQueryAsync(t => t.Id == claimSubjectDto.VisitTaskId, u => new VisitTask() { DoctorUserId = _userInfo.UserRoleId }, true); + + } + + var domainEvent = new SubjectCriterionClaimCancelEvent() { VisitTaskId = claimSubjectDto.VisitTaskId, IsInOrder = claimSubjectDto.IsInOrder }; + await _scheduler.SchedulePublish(DateTime.Now.AddDays(1), (object)domainEvent); + + + } + } + + + } + /// /// 重置阅片时间 登录和解锁调用 /// diff --git a/IRaCIS.Core.Domain/_DomainEvent/SubjectCriterionClaimCancelEvent.cs b/IRaCIS.Core.Domain/_DomainEvent/SubjectCriterionClaimCancelEvent.cs new file mode 100644 index 000000000..3be6e7970 --- /dev/null +++ b/IRaCIS.Core.Domain/_DomainEvent/SubjectCriterionClaimCancelEvent.cs @@ -0,0 +1,23 @@ +using IRaCIS.Core.Domain.BaseModel; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Domain._DomainEvent +{ + public class SubjectCriterionClaimCancelEvent : DomainEvent + { + public Guid? SubjectId { get; set; } + + public Guid? TrialReadingCriterionId { get; set; } + + + public Guid? VisitTaskId { get; set; } + + + public bool IsInOrder { get; set; } + + } +}