using IRaCIS.Application.Interfaces; using IRaCIS.Application.Contracts; using IRaCIS.Core.Infra.EFCore; using IRaCIS.Core.Application.Filter; using IRaCIS.Core.Domain.Share; using Microsoft.AspNetCore.Mvc; using IRaCIS.Core.Application.Service.Inspection.Interface; using Newtonsoft.Json; namespace IRaCIS.Application.Services { [ApiExplorerSettings(GroupName = "Trial")] public class SubjectService : BaseService, ISubjectService { private readonly IRepository _subjectRepository; private readonly IInspectionService _inspectionService; public SubjectService(IRepository subjectRepository, IInspectionService inspectionService) { _subjectRepository = subjectRepository; this._inspectionService = inspectionService; } /// /// 添加或更新受试者信息[New] /// /// state:1-访视中,2-出组。0-全部 /// [TrialAudit(AuditType.SubjectAudit, AuditOptType.AddOrUpdateSubject)] [TypeFilter(typeof(TrialResourceFilter))] public async Task> AddOrUpdateSubject([FromBody] SubjectCommand subjectCommand) { bool isadd = false; if (await _repository.AnyAsync(t => t.Id == subjectCommand.TrialId && !t.VisitPlanConfirmed)) { return ResponseOutput.NotOk("The trial visit plan has not been confirmed yet.Please contact the project manager to confirm the visit plan before adding subject."); } var verifyExp1 = new EntityVerifyExp() { VerifyExp = u => u.Code == subjectCommand.Code && u.TrialId == subjectCommand.TrialId, VerifyMsg = "A subjects with the same subject ID already existed in this trial." }; var mapedSubject = await _subjectRepository.InsertOrUpdateAsync(subjectCommand, false, verifyExp1/*, verifyExp2*/); if (subjectCommand.Id == null) //insert { isadd = true; var IsEnrollementQualificationConfirm = await _repository.Where(t => t.Id == mapedSubject.TrialId).Select(u => u.IsEnrollementQualificationConfirm).FirstOrDefaultAsync(); //添加受试者的时候,获取访视计划列表,添加到受试者访视表。 var visitPlan = await _repository.Where(t => t.TrialId == subjectCommand.TrialId).ToListAsync(); var svlist = _mapper.Map>(visitPlan); svlist.ForEach(t => { t.TrialId = subjectCommand.TrialId; t.SiteId = subjectCommand.SiteId; t.IsEnrollmentConfirm = t.IsBaseLine ? IsEnrollementQualificationConfirm : false; }); mapedSubject.SubjectVisitList = svlist; } else //update { //变更site if (mapedSubject.SiteId != subjectCommand.SiteId) { await _repository.BatchUpdateAsync(t => t.SubjectId == mapedSubject.Id, u => new SubjectVisit() { SiteId = mapedSubject.SiteId }); await _repository.BatchUpdateAsync(t => t.SubjectId == mapedSubject.Id, u => new DicomStudy() { SiteId = mapedSubject.SiteId }); }; ////如果访视结束了 需要删除计划外未执行的访视 //if (mapedSubject.Status == SubjectStatus.EndOfVisit) //{ // await _repository.DeleteFromQueryAsync(t => t.VisitExecuted == VisitExecutedEnum.UnExecuted && t.SubjectId == mapedSubject.Id && t.InPlan == false); //} ////如果是出组了 将受试者未执行的 设置为不可用 //if (mapedSubject.Status == SubjectStatus.OutOfEnrollment) //{ // await _repository.UpdateFromQueryAsync(t => t.SubjectId == mapedSubject.Id && t.VisitExecuted == VisitExecutedEnum.UnExecuted, u => new SubjectVisit() { VisitExecuted = VisitExecutedEnum.Unavailable }); //} } await _repository.SaveChangesAsync(); var createtime = DateTime.Now.AddSeconds(1); // 添加稽查记录 if (isadd) { List datas = new List(); datas.Add(new DataInspection() { TrialId = subjectCommand.TrialId, SiteId = subjectCommand.SiteId, SubjectId = mapedSubject.Id, SubjectCode = subjectCommand.Code, IsSign=false, CreateTime = createtime, Identification = "Init|Subject|Status|Subject", JsonDetail= JsonConvert.SerializeObject(new { Status= "新增", }) }); var visittime = createtime.AddSeconds(1); foreach (var item in mapedSubject.SubjectVisitList) { datas.Add(new DataInspection() { TrialId = subjectCommand.TrialId, SiteId = subjectCommand.SiteId, SubjectId = mapedSubject.Id, SubjectCode = subjectCommand.Code, SubjectVisitId= item.Id, SubjectVisitName=item.VisitName, BlindName=item.BlindName, IsSign = false, CreateTime = visittime, Identification = "Add|Visit|Info|Visit-Image Upload", JsonDetail = JsonConvert.SerializeObject(new { VisitName = item.VisitName, VisitNum = item.VisitNum, IsBaseLine=item.IsBaseLine ? "是" : "否", VisitExecuted= "否", IsFinalVisit=item.IsFinalVisit ? "是" : "否", PDState="", IsLostVisit=item.IsLostVisit?"是":"否", }) }); datas.Add(new DataInspection() { TrialId = subjectCommand.TrialId, SiteId = subjectCommand.SiteId, SubjectId = mapedSubject.Id, SubjectCode = subjectCommand.Code, SubjectVisitId = item.Id, BlindName = item.BlindName, SubjectVisitName = item.VisitName, IsSign = false, CreateTime = visittime.AddSeconds(1), Identification = "Init|Visit|Status|Visit-Image Upload", JsonDetail = JsonConvert.SerializeObject(new { VisitName = item.VisitName, SubmitState = "", AuditState = "", }) }); } await _inspectionService.AddListInspectionRecordAsync(datas); } return ResponseOutput.Ok(mapedSubject.Id.ToString()); } [HttpPut("{trialId:guid}")] public async Task UpdateSubjectStatus(SubjectStatusChangeCommand subjectStatusChangeCommand) { var subject = await _subjectRepository.FirstOrDefaultAsync(t => t.Id == subjectStatusChangeCommand.SubjectId); if (subject == null) return Null404NotFound(subject); _mapper.Map(subjectStatusChangeCommand, subject); ////如果访视结束了 需要删除计划外未执行的访视 //if (subjectStatusChangeCommand.Status == SubjectStatus.EndOfVisit) //{ // await _repository.DeleteFromQueryAsync(t => t.VisitExecuted == VisitExecutedEnum.UnExecuted && t.SubjectId == subject.Id && t.InPlan == false); //} //如果是出组了 if (subjectStatusChangeCommand.Status == SubjectStatus.OutOfVisit) { if (subjectStatusChangeCommand.FinalSubjectVisitId != null) { if (await _repository.AnyAsync(t => t.SubjectId == subjectStatusChangeCommand.SubjectId && t.IsFinalVisit && t.Id != subjectStatusChangeCommand.FinalSubjectVisitId)) { return ResponseOutput.NotOk("该受试者已经有访视设置为末次访视,不允许将该访视设置为末次访视"); } var sv = await _repository.FirstOrDefaultAsync(t => t.Id == subjectStatusChangeCommand.FinalSubjectVisitId).IfNullThrowException(); if (await _repository.AnyAsync(t => t.SubjectId == subjectStatusChangeCommand.SubjectId && t.VisitNum > sv.VisitNum && t.SubmitState == SubmitStateEnum.ToSubmit)) { return ResponseOutput.NotOk("该受试者此访视后有影像上传,该访视不允许设置为末次访视"); } sv.IsFinalVisit = true; //末次访视后的 访视设置为不可用 await _repository.BatchUpdateAsync(t => t.SubjectId == subjectStatusChangeCommand.SubjectId && t.VisitNum > sv.VisitNum, u => new SubjectVisit() { VisitExecuted = VisitExecutedEnum.Unavailable }); } //将受试者未执行的 设置为不可用 await _repository.BatchUpdateAsync(t => t.SubjectId == subject.Id && t.VisitExecuted == VisitExecutedEnum.UnExecuted, u => new SubjectVisit() { VisitExecuted = VisitExecutedEnum.Unavailable }); } else { } await _repository.SaveChangesAsync(); return ResponseOutput.Ok(); } [Obsolete] [HttpGet("{trialId:guid}/{subjectId:guid}/{finalSubjectVisitId:guid}")] public async Task VerifySubjectFinalVisit(Guid subjectId, Guid finalSubjectVisitId) { if (await _repository.AnyAsync(t => t.SubjectId == subjectId && t.IsFinalVisit)) { return ResponseOutput.NotOk("该受试者已经有访视设置为末次访视,不允许将该访视设置为末次访视"); } var sv = (await _repository.FirstOrDefaultAsync(t => t.Id == finalSubjectVisitId)).IfNullThrowException(); if (await _repository.AnyAsync(t => t.SubjectId == subjectId && t.VisitNum > sv.VisitNum && t.SubmitState == SubmitStateEnum.ToSubmit)) { return ResponseOutput.NotOk("该受试者此访视后有影像上传,该访视不允许设置为末次访视"); } return ResponseOutput.Ok(); } [HttpDelete("{id:guid}/{trialId:guid}")] [TrialAudit(AuditType.SubjectAudit, AuditOptType.DeleteSubject)] [TypeFilter(typeof(TrialResourceFilter))] public async Task DeleteSubject(Guid id) { if (await _repository.AnyAsync(u => u.SubjectId == id && u.VisitExecuted == VisitExecutedEnum.Executed)) { return ResponseOutput.NotOk("This subject has executed a visit with uploading study images,and couldn't be deleted."); } var isSuccess = await _subjectRepository.BatchDeleteAsync(u => u.Id == id); await _repository.BatchDeleteAsync(u => u.SubjectId == id); var subvisit = await _repository.GetQueryable().Where(x=>x.SubjectId==id).ToListAsync(); List datas = new List(); foreach (var item in subvisit) { datas.Add(new DataInspection() { SiteId = item.SiteId, SubjectId = item.SubjectId, TrialId = item.TrialId, SubjectVisitId = item.Id, Identification = "Delete|Visit|Data|Visit-Image Upload", JsonDetail = JsonConvert.SerializeObject(item) }); }; await _inspectionService.AddListInspectionRecordAsync(datas); return ResponseOutput.Result(isSuccess); } /// 分页获取受试者列表[New] /// /// state:1-访视中,2-出组。0-全部 [HttpPost] public async Task, TrialSubjectConfig>> GetSubjectList(SubjectQueryParam param) { var subjectQuery = _subjectRepository.Where(u => u.TrialId == param.TrialId) .WhereIf(!string.IsNullOrWhiteSpace(param.Code), t => t.Code.Contains(param.Code)) .WhereIf(!string.IsNullOrWhiteSpace(param.Name), t => t.ShortName.Contains(param.Name)) .WhereIf(!string.IsNullOrWhiteSpace(param.Sex), t => t.Sex.Contains(param.Sex)) .WhereIf(param.Status != null, t => t.Status == param.Status) .WhereIf(param.SiteId != null, t => t.SiteId == param.SiteId) // CRC 只负责他管理site的受试者 .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator, t => t.TrialSite.CRCUserList.Any(t => t.UserId == _userInfo.Id)) .ProjectTo(_mapper.ConfigurationProvider); var pageList = await subjectQuery.ToPagedListAsync(param.PageIndex, param.PageSize, param.SortField == string.Empty ? "Code" : param.SortField, param.Asc); var trialConfig = await _repository.Where(t => t.Id == param.TrialId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync().IfNullThrowException(); return ResponseOutput.Ok(pageList, trialConfig); } /// /// 计划外访视 获取受试者选择下拉框列表 /// [HttpGet("{siteId:guid}/{trialId:guid}")] public List GetSubjectListBySiteId(Guid siteId, Guid trialId) { var query = _subjectRepository.Where(t => t.SiteId == siteId && t.TrialId == trialId && t.Status == SubjectStatus.OnVisit).Select(u => new SubjectSelect() { Code = u.Code, FirstName = u.FirstName, LastName = u.LastName, Sex = u.Sex, SubjectId = u.Id, Age = u.Age, MRN = u.MedicalNo, Status = u.Status, FirstGiveMedicineTime = u.FirstGiveMedicineTime }); return query.ToList(); } } }