using IRaCIS.Core.Infrastructure.ExpressionExtend; using IRaCIS.Application.Interfaces; using IRaCIS.Application.Contracts; using IRaCIS.Core.Infra.EFCore; using IRaCIS.Core.Application.Filter; using Microsoft.AspNetCore.Mvc; using IRaCIS.Core.Domain.Share; using IRaCIS.Core.Application.MediatR.CommandAndQueries; using Magicodes.ExporterAndImporter.Core; using Magicodes.ExporterAndImporter.Excel; using Magicodes.ExporterAndImporter.Excel.AspNetCore; using IRaCIS.Core.Application.Service.Inspection.Interface; using Newtonsoft.Json; using IRaCIS.Core.Infrastructure; using MassTransit; using IRaCIS.Core.Domain.Common; namespace IRaCIS.Application.Services { [ApiExplorerSettings(GroupName = "Trial")] public class VisitPlanService : BaseService, IVisitPlanService { private readonly IRepository _visitStageRepository; private readonly IRepository _trialRepository; private readonly IRepository _subjectVisitRepository; private readonly IRepository _influnceStatRepository; private readonly IRepository _influnceRepository; private readonly IInspectionService _inspectionService; public VisitPlanService(IRepository visitStageRepository, IRepository trialRepository, IRepository subjectVisitRepository, IRepository influnceStatRepository, IRepository visitPlanInfluenceStudy, IInspectionService inspectionService) { _visitStageRepository = visitStageRepository; _trialRepository = trialRepository; this._subjectVisitRepository = subjectVisitRepository; this._influnceStatRepository = influnceStatRepository; this._influnceRepository = visitPlanInfluenceStudy; this._inspectionService = inspectionService; } ///暂时不用 /// 获取项目访视计划 public async Task> GetTrialVisitStageList(VisitPlanQueryDTO param) { var visitStageQuery = _visitStageRepository.AsQueryable(true).Where(u => u.TrialId == param.TrialId) .WhereIf(!string.IsNullOrWhiteSpace(param.Keyword), t => t.VisitName.Contains(param.Keyword)) .ProjectTo(_mapper.ConfigurationProvider); return await visitStageQuery.ToPagedListAsync(param.PageIndex, param.PageSize, "CreateTime", param.Asc); } /// 根据项目Id,获取项目访视计划(不分页)[New] [HttpGet("{trialId:guid}")] public async Task GetVisitStageList(Guid trialId) { var query = _visitStageRepository.AsQueryable(true).Where(u => u.TrialId == trialId) .ProjectTo(_mapper.ConfigurationProvider).OrderBy(t => t.VisitNum); var list = await query.ToListAsync(); var trial = (await _repository.FirstOrDefaultAsync(t => t.Id == trialId)).IfNullThrowException(); return new VisitPlanView() { VisitPlanList = list, TimePointsPerPatient = trial.TimePointsPerPatient, VisitPlanConfirmed = trial.VisitPlanConfirmed, IsHaveFirstGiveMedicineDate = trial.IsHaveFirstGiveMedicineDate, //SubjectHasAdded = _subjectVisitRepository.Any(t => t.TrialId == trialId) }; } /// /// 获取访视计划下拉框列表 /// /// /// [HttpGet("{trialId:guid}")] public async Task> GetTrialVisitStageSelect(Guid trialId) { var query = _visitStageRepository.Where(u => u.TrialId == trialId) .ProjectTo(_mapper.ConfigurationProvider).OrderBy(t => t.VisitNum); var list = await query.ToListAsync(); return list; } /// 添加或更新访视计划某项[New] [HttpPost] [UnitOfWork] public async Task> AddOrUpdateVisitStage(VisitPlanCommand visitPlan) { if (!await _trialRepository.Where(t => t.Id == visitPlan.TrialId).IgnoreQueryFilters().AnyAsync(t => t.TrialStatusStr == StaticData.TrialOngoing || t.TrialStatusStr == StaticData.TrialInitializing)) { throw new BusinessValidationFailedException(" only in Initializing or Ongoing State can operate "); } var visitPlanList = await _visitStageRepository.Where(t => t.TrialId == visitPlan.TrialId, ignoreQueryFilters: true) .Select(t => new { t.Id, t.VisitNum, t.VisitDay }).OrderBy(t => t.VisitNum).ToListAsync(); //更新的时候,需要排除自己 if (visitPlan.Id != null) { visitPlanList = visitPlanList.Where(t => t.Id != visitPlan.Id).ToList(); } if (visitPlanList.Any(t => t.VisitNum < visitPlan.VisitNum)) { //比当前 visitNum小的 visitDay的最大值 还小 不允许添加 if (visitPlan.VisitDay <= visitPlanList.Where(t => t.VisitNum < visitPlan.VisitNum).Select(t => t.VisitDay).Max()) { throw new BusinessValidationFailedException(" For the visit plan, the VisitDay with a larger VisitNum should be larger than the VisitDay with a smaller VisitNum."); } } if (visitPlanList.Any(t => t.VisitNum > visitPlan.VisitNum)) { if (visitPlan.VisitDay >= visitPlanList.Where(t => t.VisitNum > visitPlan.VisitNum).Select(t => t.VisitDay).Min()) { throw new BusinessValidationFailedException("For the visit plan, the VisitDay with a larger VisitNum should be larger than the VisitDay with a smaller VisitNum."); } } if (visitPlan.Id == Guid.Empty || visitPlan.Id == null)//add { if (await _visitStageRepository.AnyAsync(t => t.TrialId == visitPlan.TrialId && (t.VisitName == visitPlan.VisitName || t.VisitNum == visitPlan.VisitNum), true)) { throw new BusinessValidationFailedException("A visit with the same VisitName/VisitNum already existed in the current visit plan."); } if (await _visitStageRepository.AnyAsync(t => t.TrialId == visitPlan.TrialId && t.IsBaseLine, true) && visitPlan.IsBaseLine) { throw new BusinessValidationFailedException("A visit already is baseline in the current visit plan."); } //不用前端传递的值 visitPlan.BlindName = "B" + ((int)visitPlan.VisitNum * 10).ToString("D3"); var visitPlanItem = await _visitStageRepository.InsertFromDTOAsync(visitPlan,true); #region 废弃前 // var visitPlanItem = _mapper.Map(visitPlan); //visitPlanItem.BlindName = "B" + ((int)visitPlanItem.VisitNum * 10).ToString("D3"); //var result = await _visitStageRepository.AddAsync(visitPlanItem,true); #endregion return ResponseOutput.Ok(visitPlanItem); } else//update { if (await _visitStageRepository.AnyAsync(t => t.TrialId == visitPlan.TrialId && (t.VisitName == visitPlan.VisitName || t.VisitNum == visitPlan.VisitNum) && t.Id != visitPlan.Id, true)) { throw new BusinessValidationFailedException("A visit with the same VisitName/VisitNum already existed in the current visit plan."); } if (await _visitStageRepository.AnyAsync(t => t.TrialId == visitPlan.TrialId && t.IsBaseLine && t.Id != visitPlan.Id, true) && visitPlan.IsBaseLine) { throw new BusinessValidationFailedException("A visit already is baseline in the current visit plan."); } #region 废弃前 //var stage = (await _visitStageRepository.FirstOrDefaultAsync(t => t.Id == visitPlan.Id, true)).IfNullThrowException(); ////修改是否是基线 //if (stage.IsBaseLine && stage.IsBaseLine != visitPlan.IsBaseLine) //{ // if (await _repository.Where(t => t.TrialId == visitPlan.TrialId).AnyAsync(v => v.IsBaseLine && v.SubmitState >= SubmitStateEnum.ToSubmit)) // { // throw new BusinessValidationFailedException("有CRC已经为基线上传了影像数据,不允许修改基线"); // } //} //_mapper.Map(visitPlan, stage); //stage.BlindName = "B" + ((int)visitPlan.VisitNum * 10).ToString("D3"); //await _visitStageRepository.SaveChangesAsync(); #endregion //不用前端传递的值 visitPlan.BlindName = "B" + ((int)visitPlan.VisitNum * 10).ToString("D3"); //返回的是数据库查询的数据 var stage = await _visitStageRepository.UpdateFromDTOAsync(visitPlan,true); if (stage.IsBaseLine && stage.IsBaseLine != visitPlan.IsBaseLine) { if (await _repository.Where(t => t.TrialId == visitPlan.TrialId).AnyAsync(v => v.IsBaseLine && v.SubmitState >= SubmitStateEnum.ToSubmit)) { throw new BusinessValidationFailedException("有CRC已经为基线上传了影像数据,不允许修改基线"); } } return ResponseOutput.Ok(stage); } } [UnitOfWork] [HttpPost("{trialId:guid}")] [TrialAudit(AuditType.TrialAudit, AuditOptType.ConfirmTrialVisitPlan)] public async Task ConfirmTrialVisitPlan(Guid trialId) { if (!await _trialRepository.AnyAsync(t => t.Id == trialId && (t.TrialStatusStr == StaticData.TrialInitializing || t.TrialStatusStr == StaticData.TrialOngoing))) { return ResponseOutput.NotOk("仅仅在项目初始化或者进行中时,才允许修改确认"); } if (!await _visitStageRepository.AnyAsync(t => t.TrialId == trialId && t.IsBaseLine)) { return ResponseOutput.NotOk("没有基线,不允许确认"); } if (!await _trialRepository.AnyAsync(t => t.Id == trialId && t.IsTrialBasicLogicConfirmed && t.IsTrialProcessConfirmed && t.IsTrialUrgentConfirmed)) { return ResponseOutput.NotOk("项目配置未确认,不允许确认访视计划"); } var svList = await _visitStageRepository.Where(t => t.TrialId == trialId).Select(u => new { u.VisitDay, u.IsBaseLine }).ToListAsync(); if (svList.Min(t => t.VisitDay) != svList.Where(t => t.IsBaseLine).FirstOrDefault()?.VisitDay) { return ResponseOutput.NotOk("基线VisitDay 不是最小的, 不允许确认"); } //更新项目访视计划状态为已确认 必定生成更新的sql 通过状态改变 触发操作 await _trialRepository.UpdatePartialNowNoQueryAsync(trialId, t => new Trial() { VisitPlanConfirmed = true }); //找到访视计划修改的Item var changedList = await _visitStageRepository.Where(t => t.TrialId == trialId && t.IsConfirmed == false) .Select(t => new { t.Trial.IsHaveFirstGiveMedicineDate, t.Id, t.VisitName, t.TrialId, t.VisitWindowLeft, t.VisitWindowRight, t.VisitDay, t.VisitNum, t.IsBaseLine, t.BlindName, t.Description, IsConfirmed = true, }).ToListAsync(); List datas = new List(); var createtime = DateTime.Now.AddSeconds(1); changedList.ForEach(x => { datas.Add(new DataInspection() { IsSign = false, CreateTime = createtime, //SubjectVisitName = x.VisitName, VisitStageId = x.Id, TrialId = x.TrialId, JsonDetail = x.ToJcJson(), Identification = "Confirm|Visit Plan Template|Data|Trial Setting-Visit Plan", }); }); //foreach (var changedItem in changedList) //{ // var list = await _subjectVisitRepository.Where(t => t.TrialId == trialId && t.VisitStageId == changedItem.Id).ToListAsync(); // list.ForEach(x => // { // datas.Add(new DataInspection() // { // BlindName = x.BlindName, // IsSign = false, // SiteId = x.SiteId, // SubjectId = x.SubjectId, // SubjectVisitId = x.Id, // CreateTime = createtime, // SubjectVisitName = x.VisitName, // TrialId = x.TrialId, // Reason = "确认访视修改状态", // JsonDetail = JsonConvert.SerializeObject(new // { // IsBaseLine = changedItem.IsBaseLine, // VisitName = changedItem.VisitName, // VisitNum = changedItem.VisitNum, // VisitDay = changedItem.VisitDay, // SubmitState = x.SubmitState, // VisitWindowLeft = changedItem.VisitWindowLeft, // VisitWindowRight = changedItem.VisitWindowRight // }), // Identification = "Edit|Visit|Info|Visit-Image Upload" // }); // }); //} var subjectsids = _repository.GetQueryable().Where(x => x.TrialId == trialId).Select(x => new { x.Code, x.SiteId, x.Id, x.IsEnrollment, x.IsUrgent, }); List subjectVisits = new List(); var trial = await _repository.GetQueryable().FirstOrDefaultAsync(x => x.Id == trialId); var addvisitStages = await _visitStageRepository.Where(x => !x.IsHaveFirstConfirmed && x.TrialId == trialId).ToListAsync(); addvisitStages.ForEach(x => { subjectsids.ForEach(y => { Guid dataindtid = NewId.NextGuid(); Guid guid = NewId.NextGuid(); SubjectVisit subjectVisit = new SubjectVisit() { SiteId = y.SiteId, SubjectId = y.Id, Id = guid, VisitName = x.VisitName, BlindName = x.BlindName, IsBaseLine = x.IsBaseLine, IsUrgent = false, }; subjectVisits.Add(subjectVisit); // datas.Add(new DataInspection() { Id = dataindtid, BlindName = x.BlindName, IsSign = false, SiteId = y.SiteId, SubjectId = y.Id, SubjectVisitId = guid, CreateTime = createtime.AddMilliseconds(200), SubjectVisitName = x.VisitName, TrialId = x.TrialId, Identification = "Add|Visit|Info|Visit-Image Upload", JsonDetail = JsonConvert.SerializeObject(new { IsBaseLine = x.IsBaseLine, VisitName = x.VisitName, VisitNum = x.VisitNum, VisitDay = x.VisitDay, VisitExecuted = subjectVisit.VisitExecuted, BlindName = x.BlindName, VisitWindowLeft = x.VisitWindowLeft, VisitWindowRight = x.VisitWindowRight, IsEnrollment = y.IsEnrollment, IsUrgent = false, IsFinalVisit = subjectVisit.IsFinalVisit, IsLostVisit = subjectVisit.IsLostVisit, PDState = false, }), }); // 初始化受试者 datas.Add(new DataInspection() { SiteId = y.SiteId, SubjectId = y.Id, SubjectVisitId = guid, SubjectCode = y.Code, BlindName = x.BlindName, //ParentId = dataindtid, SubjectVisitName = x.VisitName, IsSign = false, TrialId = x.TrialId, CreateTime = createtime.AddMilliseconds(500), Identification = "Init|Visit|Status|Visit-Image Upload", JsonDetail = JsonConvert.SerializeObject(new { BlindName = x.BlindName, VisitName = x.VisitName, SubmitState = subjectVisit.SubmitState, AuditState = subjectVisit.AuditState, IsBaseLine = x.IsBaseLine, IsEnrollment = y.IsEnrollment, IsUrgent = y.IsUrgent, VisitNum = x.VisitNum, VisitDay = x.VisitDay, VisitExecuted = subjectVisit.VisitExecuted, VisitWindowLeft = x.VisitWindowLeft, VisitWindowRight = x.VisitWindowRight, IsFinalVisit = subjectVisit.IsFinalVisit, IsLostVisit = subjectVisit.IsLostVisit, PDState = false, }), }); }); }); await _inspectionService.AddListInspectionRecordAsync(datas); //await _subjectVisitRepository.AddRangeAsync() await _repository.AddRangeAsync(subjectVisits); //访视计划 整体状态变更为 确认 await _visitStageRepository.BatchUpdateNoTrackingAsync(u => u.TrialId == trialId, t => new VisitStage() { IsConfirmed = true, IsHaveFirstConfirmed = true }); await _repository.SaveChangesAsync(); return ResponseOutput.Ok(); } [HttpGet("{trialId:guid}")] public async Task> GetInfluenceHistoryList(Guid trialId, [FromServices] IRepository _influnceStatRepository) { var list = await _influnceStatRepository.Where(t => t.TrialId == trialId).ProjectTo(_mapper.ConfigurationProvider).OrderByDescending(t => t.CreateTime).ToListAsync(); return list; } [HttpGet("{visitPlanInfluenceStatId:guid}")] public async Task DownloadInflunceStudyList(Guid visitPlanInfluenceStatId) { var list = _influnceRepository.Where(t => t.VisitPlanInfluenceStatId == visitPlanInfluenceStatId) .ProjectTo(_mapper.ConfigurationProvider).ToList(); IExporter exporter = new ExcelExporter(); var result = await exporter.ExportAsByteArray(list); return new XlsxFileResult(bytes: result, fileDownloadName: $"检查导出_{DateTime.Now.ToString("yyyy-MM-dd:hh:mm:ss")}.xlsx"); } /// 删除项目计划某一项 废弃 [HttpDelete("{id:guid}/{trialId:guid}")] [TrialAudit(AuditType.TrialAudit, AuditOptType.DeleteTrialVisitPlanItem)] [TypeFilter(typeof(TrialResourceFilter))] [Obsolete] public async Task DeleteVisitStage(Guid id) { var visitPlan = await _visitStageRepository.FirstOrDefaultAsync(t => t.Id == id); if (visitPlan == null) return Null404NotFound(visitPlan); if (await _repository.AnyAsync(t => t.VisitName == visitPlan.VisitName && t.TrialId == visitPlan.TrialId && t.VisitExecuted == VisitExecutedEnum.Executed)) { return ResponseOutput.NotOk("The visit plan has been assigned to the subjects and executed."); } await _repository.BatchDeleteAsync(t => t.TrialId == visitPlan.TrialId && t.VisitName == visitPlan.VisitName); var result = await _visitStageRepository.BatchDeleteNoTrackingAsync(u => u.Id == id); return ResponseOutput.Result(result); } } }