using IRaCIS.Application.Interfaces; using IRaCIS.Core.Application.Contracts; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using IRaCIS.Core.Domain.Share; using EasyCaching.Core; using IRaCIS.Core.Infrastructure; namespace IRaCIS.Core.Application { [ApiExplorerSettings(GroupName = "Trial")] public class TrialConfigService : BaseService, ITrialConfigService { private readonly IRepository _trialRepository; private readonly IRepository _trialQCQuestionRepository; private readonly IEasyCachingProvider _provider; public TrialConfigService(IRepository trialRepository, IRepository trialQCQuestionRepository, IEasyCachingProvider provider ) { _trialRepository = trialRepository; this._trialQCQuestionRepository = trialQCQuestionRepository; this._provider = provider; } /// /// 获取签名文本 /// /// /// [HttpGet("{signCode}")] public async Task GetSignText(string signCode) { var signRawText = await _repository.Where(t => t.Code == signCode).Select(t => new { t.Code, t.Value, t.Id, ParentValue = t.Parent.Value, ParentValueCN = t.Parent.ValueCN }).FirstOrDefaultAsync(); if (signRawText == null) { return ResponseOutput.NotOk("未在系统中找到该签名场景的数据"); } return ResponseOutput.Ok(new { SignCodeId = signRawText.Id, SignCode = signRawText.Code, SignText = signRawText.ParentValue.Replace("xxx", signRawText.Value), SignTextCN = signRawText.ParentValueCN.Replace("xxx", signRawText.Value) }); } //验证 仅仅在 Initializing/Ongoing 才可以操作 private async Task VerifyOnlyInOngoingOrInitialIzingOptAsync(Guid trialId) { if (!await _trialRepository.AnyAsync(t => t.Id == signConfirmDTO.TrialId && (t.TrialStatusStr == StaticData.TrialInitializing || t.TrialStatusStr == StaticData.TrialOngoing))) { throw new BusinessValidationFailedException("项目不在Initializing/Ongoing,不允许确认配置"); } } /// /// 签名确认 包括项目的三组配置 + QC问题确认 后修改状态 (适用于不会回退的,项目废除、状态修改, 存在回退 不在这里弄,提供单独接口修改状态) /// /// public async Task TrialConfigSignatureConfirm(SignConfirmDTO signConfirmDTO) { await VerifyOnlyInOngoingOrInitialIzingOptAsync(signConfirmDTO.TrialId); if (signConfirmDTO.SignCode == ((int)SignEnum.TrialLogicConfim).ToString()) { await _trialRepository.UpdatePartialFromQueryAsync(t => t.Id == signConfirmDTO.TrialId, u => new Trial() { IsTrialBasicLogicConfirmed = true }); } else if (signConfirmDTO.SignCode == ((int)SignEnum.TrialProcessConfim).ToString()) { await _trialRepository.UpdatePartialFromQueryAsync(t => t.Id == signConfirmDTO.TrialId, u => new Trial() { IsTrialProcessConfirmed = true }); } else if (signConfirmDTO.SignCode == ((int)SignEnum.TrialUrgentConfim).ToString()) { await _trialRepository.UpdatePartialFromQueryAsync(t => t.Id == signConfirmDTO.TrialId, u => new Trial() { IsTrialUrgentConfirmed = true }); } else if (signConfirmDTO.SignCode == ((int)SignEnum.TrialQCQuestionConfirm).ToString()) { var trialConfig = (await _trialRepository .Select(t => new { TrialId = t.Id, t.QCProcessEnum, t.IsImageConsistencyVerification }) .FirstOrDefaultAsync(t => t.TrialId == signConfirmDTO.TrialId)) .IfNullThrowException(); var showOrderList = await _repository.Where(t => t.TrialId == signConfirmDTO.TrialId).Select(t => new { t.ShowOrder, ParentShowOrder = (int?)t.ParentQCQuestion.ShowOrder }).ToListAsync(); if (trialConfig.QCProcessEnum == TrialQCProcess.DoubleAudit || trialConfig.QCProcessEnum == TrialQCProcess.SingleAudit) { if (showOrderList.Count == 0) { throw new BusinessValidationFailedException("请先添加影像质控审核问题,再确认"); } } if (showOrderList.Count() != showOrderList.Select(t => t.ShowOrder).Distinct().Count()) { throw new BusinessValidationFailedException("QC问题显示序号不允许重复"); } if (showOrderList.Where(t => t.ParentShowOrder != null).Any(t => t.ParentShowOrder > t.ShowOrder)) { throw new BusinessValidationFailedException("父问题的序号要比子问题序号小,请确认"); } if (await _trialRepository.AnyAsync(t => t.Id == signConfirmDTO.TrialId && t.QCQuestionConfirmedUserId != null && t.QCQuestionConfirmedUserId != _userInfo.Id)) { throw new BusinessValidationFailedException("QC审核问题已被其他QC确认,不允许再次确认"); } await _trialQCQuestionRepository.UpdatePartialFromQueryAsync(t => t.TrialId == signConfirmDTO.TrialId, x => new TrialQCQuestion { IsConfirm = true }); await _trialRepository.UpdatePartialFromQueryAsync(t => t.Id == signConfirmDTO.TrialId, u => new Trial() { QCQuestionConfirmedTime = DateTime.Now, QCQuestionConfirmedUserId = _userInfo.Id, IsQCQuestionConfirmed = true }); await _trialRepository.SaveChangesAsync(); } return ResponseOutput.Ok(); } /// /// 配置 基础逻辑信息 /// /// /// [HttpPut] public async Task ConfigTrialBasicInfo(BasicTrialConfig trialConfig) { await VerifyOnlyInOngoingOrInitialIzingOptAsync(signConfirmDTO.TrialId); var trialInfo = (await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialConfig.TrialId)).IfNullThrowException(); _mapper.Map(trialConfig, trialInfo); trialInfo.UpdateTime = DateTime.Now; return ResponseOutput.Ok(await _repository.SaveChangesAsync()); } [HttpGet("{trialId:guid}")] public async Task IfTrialCanOngoing(Guid trialId) { var canOPt = await _trialRepository.AnyAsync(trial => trial.Id == trialId && trial.IsTrialBasicLogicConfirmed && trial.IsTrialProcessConfirmed && trial.IsTrialUrgentConfirmed && trial.VisitPlanConfirmed); return ResponseOutput.Ok(canOPt, msg: canOPt ? "" : "项目 基础配置、流程配置、加急配置 、访视计划,有未确认项"); } /// /// 更新项目状态 /// /// /// /// /// [HttpPut("{trialId:guid}/{trialStatusStr}/{reason?}")] [UnitOfWork] public async Task UpdateTrialState(Guid trialId, string trialStatusStr, string? reason) { var trial = (await _trialRepository.Where(t => t.Id == trialId, true).IgnoreQueryFilters().FirstOrDefaultAsync()).IfNullThrowException(); if (trialStatusStr == StaticData.TrialOngoing) { if (trial.IsTrialBasicLogicConfirmed && trial.IsTrialProcessConfirmed && trial.IsTrialUrgentConfirmed && trial.VisitPlanConfirmed) { } else { return ResponseOutput.NotOk("项目 基础配置、流程配置、加急配置 、访视计划,有未确认项"); } } if (trialStatusStr != "Ongoing") { trial.TrialFinishTime = DateTime.Now; } trial.TrialStateChangeList.Add(new TrialStateChange() { OriginState = trial.TrialStatusStr, NowState = trialStatusStr, Reason = reason ?? String.Empty, TrialId = trialId }); trial.TrialStatusStr = trialStatusStr; //Paused、 添加工总量 算医生读片中 if (trialStatusStr.Contains(StaticData.TrialCompleted)) { await _repository.BatchUpdateAsync(u => u.TrialId == trialId, e => new Enroll { EnrollStatus = (int)EnrollStatus.Finished }); await _trialRepository.BatchUpdateNoTrackingAsync(u => u.Id == trialId, s => new Trial { TrialFinishedTime = DateTime.UtcNow.AddHours(8) }); } await _provider.SetAsync(trialId.ToString(), trialStatusStr, TimeSpan.FromDays(7)); await _repository.SaveChangesAsync(); //var success = await _repository.BatchUpdateAsync(t => t.Id == signId, u => new TrialSign() { IsCompleted = true }); return ResponseOutput.Ok(); } /// /// 项目状态 变更历史 /// /// /// [HttpGet("{trialId:guid}")] public async Task> GetTrialStateChangeList(Guid trialId) { return await _repository.Where(t => t.TrialId == trialId).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); } /// /// 废除项目 /// /// /// /// [HttpPut("{trialId:guid}/{isAbandon:bool}")] public async Task AbandonTrial(Guid trialId, /*Guid? signId,*/ bool isAbandon) { await _trialRepository.UpdatePartialFromQueryAsync(trialId, u => new Trial() { IsDeleted = isAbandon, TrialFinishTime = isAbandon ? DateTime.Now : null }, true); return ResponseOutput.Ok(); } /// /// 获取 配置的所有信息 没有分多个接口 /// /// /// [HttpGet("{trialId:guid}")] public async Task GetTrialConfigInfo(Guid trialId) { return await _trialRepository.Where(t => t.Id == trialId).ProjectTo(_mapper.ConfigurationProvider) .FirstOrDefaultAsync().IfNullThrowException(); } /// /// 配置流程 /// /// /// [HttpPut] public async Task ConfigTrialProcessInfo(TrialProcessConfig trialConfig) { if (!await _trialRepository.Where(t => t.Id == trialConfig.TrialId).IgnoreQueryFilters().AnyAsync(t => t.TrialStatusStr == StaticData.TrialInitializing)) { return ResponseOutput.NotOk(" only in Initializing State can operate "); } var trialInfo = await _trialRepository.Where(t => t.Id == trialConfig.TrialId, true).Include(t => t.TrialDicList.Where(u => u.KeyName == StaticData.Criterion)).FirstOrDefaultAsync(); if (trialInfo == null) return Null404NotFound(trialInfo); _mapper.Map(trialConfig, trialInfo); trialInfo.UpdateTime = DateTime.Now; return ResponseOutput.Ok(await _repository.SaveChangesAsync()); } /// /// 配置加急信息 /// /// /// [HttpPut] public async Task ConfigTrialUrgentInfo(TrialUrgentConfig trialConfig) { await VerifyOnlyInOngoingOrInitialIzingOptAsync(trialConfig.TrialId); var trialInfo = (await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialConfig.TrialId)).IfNullThrowException(); //项目紧急 当前所有已提交,但未完成的访视,设置为加急。后续提交的访视也设置为加急 (在提交的时候,回去判断 项目加急,Subject加急,是否入组确认,是否Pd,从而设置访视是否加急) if (trialConfig.IsUrgent) { await _repository.BatchUpdateAsync(t => t.TrialId == trialInfo.Id && t.SubmitState == SubmitStateEnum.Submitted && t.ForwardState < ForwardStateEnum.Forwarded, s => new SubjectVisit() { IsUrgent = trialConfig.IsUrgent }); } else //之前设置为加急的访视状态不变。后续提交的访视,为不加急。 { } _mapper.Map(trialConfig, trialInfo); trialInfo.UpdateTime = DateTime.Now; return ResponseOutput.Ok(await _repository.SaveChangesAsync()); } #region 调整废弃 [Obsolete] [HttpPost("{trialId:guid}/{type}")] public async Task UploadTrialFile(IFormFile file, string type, Guid trialId, [FromServices] IWebHostEnvironment _hostEnvironment) { var rootPath = Directory.GetParent(_hostEnvironment.ContentRootPath.TrimEnd('\\')).IfNullThrowException().FullName; //上传根路径 var _fileStorePath = Path.Combine(rootPath, StaticData.TrialDataFolder); //文件类型路径处理 var uploadFolderPath = Path.Combine(_fileStorePath, trialId.ToString(), type); if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); var realName = file.FileName; var fileNameEX = Path.GetExtension(realName); var trustedFileNameForFileStorage = Guid.NewGuid().ToString() + fileNameEX; var relativePath = $"/{StaticData.TrialDataFolder}/{trialId}/{type}/{trustedFileNameForFileStorage}"; var filePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); using (FileStream fs = System.IO.File.Create(filePath)) { await file.CopyToAsync(fs); fs.Flush(); } return ResponseOutput.Ok(new { FilePath = relativePath, FullFilePath = relativePath + "?access_token=" + _userInfo.UserToken }); } /// /// 签名认证 + /// /// [HttpPost] [Obsolete] public async Task VerifySignature(SignDTO signDTO) { var user = await _repository.FirstOrDefaultAsync(u => u.UserName == signDTO.UserName && u.Password == signDTO.PassWord); if (user == null) { return ResponseOutput.NotOk("password error"); } else if (user.Status == UserStateEnum.Disable) { return ResponseOutput.NotOk("The user has been disabled!"); } //if (signDTO.IsAddSignData) //{ // 记录签名信息 var add = await _repository.AddAsync(_mapper.Map(signDTO)); await _repository.SaveChangesAsync(); return ResponseOutput.Ok(add.Id); //} //return ResponseOutput.Ok(); } /// /// 业务接口操作成功后, 让签署数据生效 /// /// /// [HttpPut("{signId:guid}")] [Obsolete] public async Task MakeSignEffective(Guid signId) { var success = await _repository.BatchUpdateAsync(t => t.Id == signId, u => new TrialSign() { IsCompleted = true }); return ResponseOutput.Result(success); } #endregion } }