diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/appservice/IConsultationAppService.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/appservice/IConsultationAppService.java index ba1719be..4c907756 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/appservice/IConsultationAppService.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/appservice/IConsultationAppService.java @@ -1,8 +1,6 @@ package com.openhis.web.consultation.appservice; -import com.openhis.web.consultation.dto.ConsultationActivityDto; -import com.openhis.web.consultation.dto.ConsultationRequestDto; -import com.openhis.web.consultation.dto.DepartmentTreeDto; +import com.openhis.web.consultation.dto.*; import java.util.List; import java.util.Map; @@ -23,6 +21,24 @@ public interface IConsultationAppService { */ List getConsultationList(Long encounterId); + /** + * 查询会诊申请列表(支持多条件查询) + * + * @param dto 查询条件DTO + * @return 会诊列表 + */ + List queryConsultationList(ConsultationRequestDto dto); + + /** + * 分页查询会诊申请列表(支持多条件查询) + * + * @param dto 查询条件DTO + * @param pageNum 页码 + * @param pageSize 每页大小 + * @return 分页结果 + */ + com.baomidou.mybatisplus.extension.plugins.pagination.Page queryConsultationListPage(ConsultationRequestDto dto, Integer pageNum, Integer pageSize); + /** * 保存会诊申请 * @@ -84,6 +100,55 @@ public interface IConsultationAppService { * @return 会诊项目列表 */ List getConsultationActivities(); + + // ==================== 会诊确认相关接口 ==================== + + /** + * 获取待确认的会诊列表(当前医生被邀请的会诊) + * + * @return 待确认的会诊列表 + */ + List getPendingConfirmationList(); + + /** + * 确认会诊 + * + * @param dto 会诊确认DTO + * @return 是否成功 + */ + Boolean confirmConsultation(ConsultationConfirmationDto dto); + + /** + * 取消确认会诊 + * + * @param consultationId 会诊申请单号 + * @return 是否成功 + */ + Boolean cancelConfirmation(String consultationId); + + /** + * 签名会诊 + * + * @param consultationId 会诊申请单号 + * @return 是否成功 + */ + Boolean signConsultation(String consultationId); + + /** + * 获取会诊确认详情 + * + * @param consultationId 会诊申请单号 + * @return 会诊确认详情 + */ + ConsultationConfirmationDto getConfirmationDetail(String consultationId); + + /** + * 获取会诊意见列表 + * + * @param consultationId 会诊申请单号 + * @return 会诊意见列表 + */ + List getConsultationOpinions(String consultationId); } diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/appservice/impl/ConsultationAppServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/appservice/impl/ConsultationAppServiceImpl.java index b5b3c6cb..6db0549c 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/appservice/impl/ConsultationAppServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/appservice/impl/ConsultationAppServiceImpl.java @@ -2,6 +2,7 @@ package com.openhis.web.consultation.appservice.impl; import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.core.common.utils.SecurityUtils; import com.openhis.administration.domain.Encounter; import com.openhis.administration.domain.EncounterDiagnosis; @@ -24,16 +25,19 @@ import com.core.common.utils.AssignSeqUtil; import com.openhis.common.enums.AssignSeqEnum; import com.openhis.web.consultation.appservice.IConsultationAppService; import com.openhis.web.consultation.dto.ConsultationActivityDto; +import com.openhis.web.consultation.dto.ConsultationConfirmationDto; +import com.openhis.web.consultation.dto.ConsultationOpinionDto; import com.openhis.web.consultation.dto.ConsultationRequestDto; import com.openhis.web.consultation.dto.DepartmentTreeDto; import com.openhis.web.consultation.dto.InvitedObjectDto; import com.openhis.web.consultation.dto.PhysicianNodeDto; +import com.openhis.web.consultation.mapper.ConsultationRequestMapper; import com.openhis.web.consultation.mapper.ConsultationInvitedMapper; -import com.openhis.consultation.mapper.ConsultationRequestMapper; -import com.openhis.consultation.domain.ConsultationRequest; +import com.openhis.web.consultation.mapper.ConsultationConfirmationMapper; +import com.openhis.web.consultation.domain.ConsultationRequest; import com.openhis.web.consultation.domain.ConsultationInvited; +import com.openhis.web.consultation.domain.ConsultationConfirmation; import com.openhis.web.consultation.enums.ConsultationStatusEnum; -import com.openhis.web.consultation.enums.InvitedStatusEnum; import com.openhis.web.consultation.enums.ConsultationUrgencyEnum; import com.openhis.workflow.domain.ActivityDefinition; import com.openhis.workflow.mapper.ActivityDefinitionMapper; @@ -68,10 +72,13 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { @Resource private ConsultationRequestMapper consultationRequestMapper; - + @Resource private ConsultationInvitedMapper consultationInvitedMapper; + @Resource + private ConsultationConfirmationMapper consultationConfirmationMapper; + @Resource private EncounterMapper encounterMapper; @@ -86,25 +93,25 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { @Resource private PractitionerMapper practitionerMapper; - + @Resource private EncounterDiagnosisMapper encounterDiagnosisMapper; - + @Resource private IServiceRequestService iServiceRequestService; - + @Resource private AssignSeqUtil assignSeqUtil; - + @Resource private ActivityDefinitionMapper activityDefinitionMapper; - + @Resource private ChargeItemDefinitionMapper chargeItemDefinitionMapper; - + @Resource private IChargeItemService iChargeItemService; - + @Resource private DoctorStationAdviceAppMapper doctorStationAdviceAppMapper; @@ -113,7 +120,7 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { * 每秒重置,范围 0000-9999 */ private static final AtomicInteger SEQUENCE = new AtomicInteger(0); - + /** * 记录上一次生成ID的时间(秒级) */ @@ -121,49 +128,160 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { @Override public List getConsultationList(Long encounterId) { + // 查询会诊列表 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + + // 根据就诊ID,查询该患者的会诊申请 + wrapper.eq(ConsultationRequest::getEncounterId, encounterId); + wrapper.orderByDesc(ConsultationRequest::getCreateTime); + + List list = consultationRequestMapper.selectList(wrapper); + // 转换为DTO + return list.stream().map(this::convertToDto).collect(Collectors.toList()); + } + + @Override + public List queryConsultationList(ConsultationRequestDto dto) { try { - log.info("开始查询会诊列表,encounterId: {}", encounterId); - - // 查询会诊列表 LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); - - // 根据就诊ID,查询该患者的会诊申请 - wrapper.eq(ConsultationRequest::getEncounterId, encounterId); - wrapper.orderByDesc(ConsultationRequest::getCreateTime); - + + // 只查询当前租户的数据 + wrapper.eq(ConsultationRequest::getTenantId, SecurityUtils.getLoginUser().getTenantId().longValue()); + + // 时间范围查询(根据前端选择的时间类型) + // 注意:前端传递的 timeType 字段不在实体中,需要在 DTO 中添加 + // 这里默认按会诊时间查询 + if (dto.getConsultationRequestDate() != null) { + // 如果传递了开始时间,按开始时间查询 + wrapper.ge(ConsultationRequest::getConsultationDate, dto.getConsultationRequestDate()); + } + + // 申请科室模糊查询 + if (StringUtils.hasText(dto.getDepartment())) { + wrapper.like(ConsultationRequest::getDepartment, dto.getDepartment()); + } + + // 申请医生模糊查询 + if (StringUtils.hasText(dto.getRequestingPhysician())) { + wrapper.like(ConsultationRequest::getRequestingPhysician, dto.getRequestingPhysician()); + } + + // 会诊状态查询 + if (dto.getConsultationStatus() != null) { + wrapper.eq(ConsultationRequest::getConsultationStatus, dto.getConsultationStatus()); + } + + // 紧急程度查询 + if (StringUtils.hasText(dto.getConsultationUrgency())) { + wrapper.eq(ConsultationRequest::getConsultationUrgency, dto.getConsultationUrgency()); + } + + // 病人姓名模糊查询 + if (StringUtils.hasText(dto.getPatientName())) { + wrapper.like(ConsultationRequest::getPatientName, dto.getPatientName()); + } + + // 按创建时间倒序排列 + wrapper.orderByDesc(ConsultationRequest::getConsultationRequestDate); + List list = consultationRequestMapper.selectList(wrapper); - log.info("查询到 {} 个会诊申请记录", list.size()); - + // 转换为DTO - List result = list.stream().map(this::convertToDto).collect(Collectors.toList()); - return result; + return list.stream().map(this::convertToDto).collect(Collectors.toList()); } catch (Exception e) { - log.error("获取会诊列表失败,encounterId: {}", encounterId, e); - // 返回空列表而不是抛出异常,避免前端崩溃 - return new ArrayList<>(); + log.error("查询会诊申请列表失败", e); + throw new RuntimeException("查询会诊申请列表失败: " + e.getMessage()); + } + } + + @Override + public Page queryConsultationListPage(ConsultationRequestDto dto, Integer pageNum, Integer pageSize) { + try { + // 设置分页参数 + Page page = new Page<>(pageNum, pageSize); + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + + // 只查询当前租户的数据 + wrapper.eq(ConsultationRequest::getTenantId, SecurityUtils.getLoginUser().getTenantId().longValue()); + + // 时间范围查询 + if (dto.getConsultationRequestDate() != null) { + wrapper.ge(ConsultationRequest::getConsultationDate, dto.getConsultationRequestDate()); + } + + // 申请科室模糊查询 + if (StringUtils.hasText(dto.getDepartment())) { + wrapper.like(ConsultationRequest::getDepartment, dto.getDepartment()); + } + + // 申请医生模糊查询 + if (StringUtils.hasText(dto.getRequestingPhysician())) { + wrapper.like(ConsultationRequest::getRequestingPhysician, dto.getRequestingPhysician()); + } + + // 会诊状态查询 + if (dto.getConsultationStatus() != null) { + wrapper.eq(ConsultationRequest::getConsultationStatus, dto.getConsultationStatus()); + } + + // 紧急程度查询 + if (StringUtils.hasText(dto.getConsultationUrgency())) { + wrapper.eq(ConsultationRequest::getConsultationUrgency, dto.getConsultationUrgency()); + } + + // 病人姓名模糊查询 + if (StringUtils.hasText(dto.getPatientName())) { + wrapper.like(ConsultationRequest::getPatientName, dto.getPatientName()); + } + + // 按创建时间倒序排列 + wrapper.orderByDesc(ConsultationRequest::getConsultationRequestDate); + + // 执行分页查询 + Page resultPage = consultationRequestMapper.selectPage(page, wrapper); + + // 转换为DTO分页结果 + Page dtoPage = new Page<>(resultPage.getCurrent(), resultPage.getSize(), resultPage.getTotal()); + List dtoList = resultPage.getRecords().stream() + .map(this::convertToDto) + .collect(Collectors.toList()); + dtoPage.setRecords(dtoList); + + return dtoPage; + } catch (Exception e) { + log.error("分页查询会诊申请列表失败", e); + throw new RuntimeException("分页查询会诊申请列表失败: " + e.getMessage()); } } @Override @Transactional(rollbackFor = Exception.class) public Boolean saveConsultation(ConsultationRequestDto dto) { + try { // 判断是新增还是更新 boolean isUpdate = (dto.getId() != null); ConsultationRequest entity; - + if (isUpdate) { // 更新:查询现有记录 entity = consultationRequestMapper.selectById(dto.getId()); if (entity == null) { throw new RuntimeException("会诊申请记录不存在,ID: " + dto.getId()); } + + // 🎯 状态校验:只有"新开"状态的会诊申请才能修改 + ConsultationStatusEnum currentStatus = ConsultationStatusEnum.getByCode(entity.getConsultationStatus()); + if (currentStatus != null && !currentStatus.canEdit()) { + throw new IllegalArgumentException("只有新开状态的会诊申请才能修改,当前状态:" + currentStatus.getDescription()); + } } else { // 新增:创建新记录 entity = new ConsultationRequest(); entity.setConsultationId(generateConsultationId()); entity.setTenantId(SecurityUtils.getLoginUser().getTenantId().longValue()); - entity.setConsultationRequestDate(LocalDateTime.now()); + entity.setConsultationRequestDate(new Date()); } // 复制基本属性(现在字段名已统一,可以直接复制) @@ -173,15 +291,15 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { if (entity.getRequestingPhysicianId() == null) { entity.setRequestingPhysicianId(SecurityUtils.getLoginUser().getPractitionerId()); } - + // 设置邀请对象文本(拼接所有医生名称) if (dto.getInvitedList() != null && !dto.getInvitedList().isEmpty()) { String invitedObjectText = dto.getInvitedList().stream() - .map(InvitedObjectDto::getPhysicianName) - .collect(Collectors.joining(",")); + .map(InvitedObjectDto::getPhysicianName) + .collect(Collectors.joining(",")); entity.setInvitedObject(invitedObjectText); } - + // 处理门诊诊断:如果前端没有传递,自动从数据库获取 if (!StringUtils.hasText(dto.getProvisionalDiagnosis()) && dto.getEncounterId() != null) { String diagnosis = getPatientDiagnosis(dto.getEncounterId()); @@ -197,7 +315,7 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { entity.setConsultationStatus(ConsultationStatusEnum.SUBMITTED.getCode()); entity.setConfirmingPhysician(SecurityUtils.getLoginUser().getUser().getNickName()); entity.setConfirmingPhysicianId(SecurityUtils.getLoginUser().getPractitionerId()); - entity.setConfirmingDate(LocalDateTime.now()); + entity.setConfirmingDate(new Date()); } else { entity.setConsultationStatus(ConsultationStatusEnum.NEW.getCode()); } @@ -205,23 +323,23 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { // 保存或更新主表 if (isUpdate) { consultationRequestMapper.updateById(entity); - + // 删除旧的邀请对象 LambdaQueryWrapper deleteWrapper = new LambdaQueryWrapper<>(); deleteWrapper.eq(ConsultationInvited::getConsultationRequestId, entity.getId()); consultationInvitedMapper.delete(deleteWrapper); - + // 更新门诊医嘱表 if (entity.getOrderId() != null) { updateConsultationServiceRequest(entity); } } else { consultationRequestMapper.insert(entity); - + // 保存到门诊医嘱表 saveConsultationServiceRequest(entity); } - + // 保存邀请对象到关联表 if (dto.getInvitedList() != null && !dto.getInvitedList().isEmpty()) { for (InvitedObjectDto invitedDto : dto.getInvitedList()) { @@ -231,11 +349,17 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { invited.setInvitedDepartmentName(invitedDto.getDeptName()); invited.setInvitedPhysicianId(invitedDto.getPhysicianId()); invited.setInvitedPhysicianName(invitedDto.getPhysicianName()); - invited.setInvitedStatus(InvitedStatusEnum.PENDING.getCode()); + invited.setInvitedStatus(ConsultationStatusEnum.SUBMITTED.getCode()); // 待确认状态 invited.setTenantId(entity.getTenantId()); - + consultationInvitedMapper.insert(invited); } + + // 初始化邀请医生总数 + entity.setInvitedCount(dto.getInvitedList().size()); + entity.setConfirmedCount(0); + entity.setSignedCount(0); + consultationRequestMapper.updateById(entity); } return true; @@ -268,14 +392,14 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { // 更新状态为已提交 entity.setConsultationStatus(ConsultationStatusEnum.SUBMITTED.getCode()); - + // 设置提交会诊的医生信息 entity.setConfirmingPhysician(SecurityUtils.getLoginUser().getUser().getNickName()); entity.setConfirmingPhysicianId(SecurityUtils.getLoginUser().getPractitionerId()); - entity.setConfirmingDate(LocalDateTime.now()); - + entity.setConfirmingDate(new Date()); + consultationRequestMapper.updateById(entity); - + // 新增:更新门诊医嘱表状态为已提交 updateServiceRequestStatus(entity.getOrderId(), RequestStatus.ACTIVE.getValue()); return true; @@ -309,20 +433,20 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { entity.setConfirmingPhysicianId(null); entity.setConfirmingDate(null); consultationRequestMapper.updateById(entity); - + // 更新门诊医嘱表状态为新开 updateServiceRequestStatus(entity.getOrderId(), RequestStatus.DRAFT.getValue()); - + } else { // 作废:将状态改为"已取消" entity.setConsultationStatus(ConsultationStatusEnum.CANCELLED.getCode()); entity.setCancelReason(cancelReason); - entity.setCancelNatureDate(LocalDateTime.now()); + entity.setCancelNatureDate(new Date()); consultationRequestMapper.updateById(entity); - + // 更新门诊医嘱表状态为已作废 updateServiceRequestStatus(entity.getOrderId(), RequestStatus.CANCELLED.getValue()); - + } return true; @@ -331,7 +455,7 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { throw new RuntimeException("操作失败: " + e.getMessage()); } } - + @Override @Transactional(rollbackFor = Exception.class) public Boolean completeConsultation(String consultationId) { @@ -351,10 +475,10 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { // 更新状态为已完成 entity.setConsultationStatus(ConsultationStatusEnum.COMPLETED.getCode()); consultationRequestMapper.updateById(entity); - + // 🎯 新增:更新门诊医嘱表状态为已完成 updateServiceRequestStatus(entity.getOrderId(), RequestStatus.COMPLETED.getValue()); - + return true; } catch (Exception e) { @@ -371,14 +495,14 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { deptWrapper.eq(Organization::getDeleteFlag, "0") .orderByAsc(Organization::getName); List deptList = organizationMapper.selectList(deptWrapper); - + // 查询所有医生 LambdaQueryWrapper practitionerWrapper = new LambdaQueryWrapper<>(); practitionerWrapper.eq(Practitioner::getDeleteFlag, "0") .orderByAsc(Practitioner::getName); List practitionerList = practitionerMapper.selectList(practitionerWrapper); - + // 按科室分组医生 Map> practitionerMap = practitionerList.stream() @@ -391,7 +515,7 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { DepartmentTreeDto treeDto = new DepartmentTreeDto(); treeDto.setId(dept.getId()); treeDto.setLabel(dept.getName()); - + // 添加该科室的医生 List physicians = practitionerMap.get(dept.getId()); if (physicians != null && !physicians.isEmpty()) { @@ -410,7 +534,7 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { treeList.add(treeDto); } - + return treeList; } catch (Exception e) { @@ -438,10 +562,10 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { return result; } } - + /** * 获取患者诊断信息 - * + * * @param encounterId 就诊ID * @return 诊断信息字符串 */ @@ -453,61 +577,61 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { .orderByDesc(EncounterDiagnosis::getMaindiseFlag) // 主诊断排在前面 .orderByAsc(EncounterDiagnosis::getDiagSrtNo) // 按诊断排序号排序 .orderByDesc(EncounterDiagnosis::getCreateTime); // 按创建时间倒序 - + List diagnosisList = encounterDiagnosisMapper.selectList(wrapper); - + if (diagnosisList == null || diagnosisList.isEmpty()) { return null; } - + // 使用 LinkedHashSet 去重,保持顺序 LinkedHashSet diagnosisSet = new LinkedHashSet<>(); int mainDiagnosisCount = 0; - + for (EncounterDiagnosis ed : diagnosisList) { // 优先使用 name 字段,如果没有则使用 diagnosisDesc String diagName = StringUtils.hasText(ed.getName()) ? ed.getName() : ed.getDiagnosisDesc(); - + if (StringUtils.hasText(diagName)) { // 构建诊断文本 StringBuilder itemBuilder = new StringBuilder(); itemBuilder.append(diagName); - + // 如果有诊断描述且与名称不同,添加到括号中 - if (StringUtils.hasText(ed.getDiagnosisDesc()) && - !ed.getDiagnosisDesc().equals(diagName)) { + if (StringUtils.hasText(ed.getDiagnosisDesc()) && + !ed.getDiagnosisDesc().equals(diagName)) { itemBuilder.append("(").append(ed.getDiagnosisDesc()).append(")"); } - + // 标记主诊断 if (ed.getMaindiseFlag() != null && ed.getMaindiseFlag() == 1) { itemBuilder.append("[主]"); mainDiagnosisCount++; } - + // 添加到集合(自动去重) diagnosisSet.add(itemBuilder.toString()); - + // 限制最多显示 10 个诊断(包括主诊断) if (diagnosisSet.size() >= 10) { break; } } } - + // 拼接诊断信息 String result = String.join(";", diagnosisSet); - + return result; - + } catch (Exception e) { log.error("获取患者诊断信息失败,encounterId: {}", encounterId, e); return null; } } - + @Override public List getMyInvitations() { try { @@ -518,26 +642,26 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { LambdaQueryWrapper invitedWrapper = new LambdaQueryWrapper<>(); invitedWrapper.eq(ConsultationInvited::getInvitedPhysicianId, currentPhysicianId) .orderByDesc(ConsultationInvited::getCreateTime); - + List invitedList = consultationInvitedMapper.selectList(invitedWrapper); if (invitedList.isEmpty()) { return new ArrayList<>(); } - + // 获取所有会诊申请ID List requestIds = invitedList.stream() .map(ConsultationInvited::getConsultationRequestId) .distinct() .collect(Collectors.toList()); - + // 查询会诊申请详情 List requests = consultationRequestMapper.selectBatchIds(requestIds); - + // 转换为DTO并添加邀请状态 Map invitedMap = invitedList.stream() .collect(Collectors.toMap(ConsultationInvited::getConsultationRequestId, v -> v, (v1, v2) -> v1)); - + return requests.stream().map(request -> { ConsultationRequestDto dto = convertToDto(request); // 添加我的邀请状态 @@ -548,7 +672,7 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { } return dto; }).collect(Collectors.toList()); - + } catch (Exception e) { log.error("获取我的会诊邀请失败", e); throw new RuntimeException("获取我的会诊邀请失败: " + e.getMessage()); @@ -567,7 +691,7 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { LambdaQueryWrapper invitedWrapper = new LambdaQueryWrapper<>(); invitedWrapper.eq(ConsultationInvited::getConsultationRequestId, entity.getId()); List invitedList = consultationInvitedMapper.selectList(invitedWrapper); - + if (invitedList != null && !invitedList.isEmpty()) { List invitedDtoList = invitedList.stream().map(invited -> { InvitedObjectDto invitedDto = new InvitedObjectDto(); @@ -575,13 +699,52 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { invitedDto.setDeptName(invited.getInvitedDepartmentName()); invitedDto.setPhysicianId(invited.getInvitedPhysicianId()); invitedDto.setPhysicianName(invited.getInvitedPhysicianName()); + // 🎯 新增:填充邀请状态、确认时间、签名时间 + invitedDto.setInvitedStatus(invited.getInvitedStatus()); + invitedDto.setConfirmTime(invited.getConfirmTime()); + invitedDto.setSignatureTime(invited.getSignatureTime()); return invitedDto; }).collect(Collectors.toList()); - + dto.setInvitedList(invitedDtoList); + + // 🎯 如果会诊已完成或已签名,填充会诊记录信息(从已签名的医生中获取) + if (entity.getConsultationStatus() != null && + (entity.getConsultationStatus() == ConsultationStatusEnum.SIGNED.getCode() || + entity.getConsultationStatus() == ConsultationStatusEnum.COMPLETED.getCode())) { + + // 查询所有已签名的医生(invited_status >= 3) + List signedPhysicians = invitedList.stream() + .filter(inv -> inv.getInvitedStatus() != null && inv.getInvitedStatus() >= 3) + .collect(Collectors.toList()); + + if (!signedPhysicians.isEmpty()) { + // 1. 会诊邀请参加医师:拼接所有已签名医生的"科室-姓名" + String invitedPhysiciansText = signedPhysicians.stream() + .map(inv -> inv.getInvitedDepartmentName() + "-" + inv.getInvitedPhysicianName()) + .collect(Collectors.joining("、")); + dto.setInvitedPhysiciansText(invitedPhysiciansText); + + // 2. 会诊意见:汇总所有已签名医生的意见 + String consultationOpinion = signedPhysicians.stream() + .filter(inv -> StringUtils.hasText(inv.getConfirmOpinion())) + .map(ConsultationInvited::getConfirmOpinion) + .collect(Collectors.joining("\n")); + dto.setConsultationOpinion(consultationOpinion); + + // 3. 所属医生、代表科室、签名医生、签名时间:使用第一个签名的医生 + ConsultationInvited firstSigned = signedPhysicians.get(0); + dto.setAttendingPhysician(firstSigned.getInvitedPhysicianName()); + dto.setRepresentDepartment(firstSigned.getInvitedDepartmentName()); + dto.setSignPhysician(firstSigned.getInvitedPhysicianName()); + dto.setSignTime(firstSigned.getSignatureTime()); + + log.info("填充会诊记录信息,已签名医生数:{}", signedPhysicians.size()); + } + } } } - + // 如果门诊诊断为空,尝试从数据库自动获取 if (!StringUtils.hasText(entity.getProvisionalDiagnosis()) && entity.getEncounterId() != null) { String diagnosis = getPatientDiagnosis(entity.getEncounterId()); @@ -597,7 +760,7 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { * 生成会诊申请单号(高性能版本) * 格式:CS + 年月日时分秒 + 4位序列号 * 例如:CS202601301425300001 - * + *

* 优化说明: * 1. 使用 AtomicInteger 保证线程安全,性能远高于 synchronized * 2. 每秒最多支持 10000 个并发请求(0000-9999) @@ -632,8 +795,8 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { sleepUntilNextSecond(nowMillis); continue; } - java.time.format.DateTimeFormatter formatter = java.time.format.DateTimeFormatter.ofPattern("yyyyMMddHHmmss"); - String dateTime = java.time.LocalDateTime.now().format(formatter); + java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyyMMddHHmmss"); + String dateTime = sdf.format(new Date()); String sequenceStr = String.format("%04d", sequence); return "CS" + dateTime + sequenceStr; @@ -701,37 +864,38 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { /** * 计算年龄 */ - private Integer calculateAge(java.util.Date birthDate) { + private Integer calculateAge(Date birthDate) { if (birthDate == null) { return null; } + java.util.Calendar now = java.util.Calendar.getInstance(); + java.util.Calendar birth = java.util.Calendar.getInstance(); + birth.setTime(birthDate); - java.time.LocalDate birthLocalDate = birthDate.toInstant() - .atZone(java.time.ZoneId.systemDefault()) - .toLocalDate(); - java.time.LocalDate now = java.time.LocalDate.now(); - - int age = java.time.Period.between(birthLocalDate, now).getYears(); + int age = now.get(java.util.Calendar.YEAR) - birth.get(java.util.Calendar.YEAR); + if (now.get(java.util.Calendar.DAY_OF_YEAR) < birth.get(java.util.Calendar.DAY_OF_YEAR)) { + age--; + } return age; } - + /** * 保存会诊医嘱到门诊医嘱表(wor_service_request) * 同时创建费用项(adm_charge_item) - * + * * @param consultationRequest 会诊申请实体 */ private void saveConsultationServiceRequest(ConsultationRequest consultationRequest) { try { ServiceRequest serviceRequest = new ServiceRequest(); - + // 生成医嘱编号 String busNo = assignSeqUtil.getSeqByDay(AssignSeqEnum.SERVICE_RES_NO.getPrefix(), 4); serviceRequest.setBusNo(busNo); - + // 基本信息 serviceRequest.setGenerateSourceEnum(GenerateSource.DOCTOR_PRESCRIPTION.getValue()); - + // 🎯 使用前端选择的会诊项目ID Long activityDefinitionId = consultationRequest.getConsultationActivityId(); if (activityDefinitionId == null) { @@ -739,16 +903,16 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { activityDefinitionId = 1983417749497446402L; log.warn("前端未传递会诊项目ID,使用默认的院内会诊: {}", activityDefinitionId); } - + // 🎯 设置医嘱类型为会诊(31=会诊类型) // 医嘱类型分类:1-10=药品,22-30=诊疗,31=会诊,40+=其他 serviceRequest.setCategoryEnum(31); // 会诊类型 serviceRequest.setActivityId(activityDefinitionId); // 关联到会诊项目定义 - + serviceRequest.setPatientId(consultationRequest.getPatientId()); serviceRequest.setEncounterId(consultationRequest.getEncounterId()); serviceRequest.setRequesterId(consultationRequest.getRequestingPhysicianId()); - + // 🎯 设置执行科室(申请医生的科室) serviceRequest.setOrgId(consultationRequest.getDepartmentId()); @@ -758,7 +922,7 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { // 医嘱状态:新开 serviceRequest.setStatusEnum(RequestStatus.DRAFT.getValue()); - + // 医嘱内容:保存会诊申请的详细信息(JSON格式) Map contentMap = new HashMap<>(); contentMap.put("consultationRequestId", consultationRequest.getId()); @@ -771,20 +935,20 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { contentMap.put("requestingPhysician", consultationRequest.getRequestingPhysician()); contentMap.put("department", consultationRequest.getDepartment()); contentMap.put("adviceName", consultationRequest.getConsultationActivityName()); // 添加项目名称 - + serviceRequest.setContentJson(JSON.toJSONString(contentMap)); - + // 时间信息 - serviceRequest.setAuthoredTime(new java.util.Date()); - + serviceRequest.setAuthoredTime(new Date()); + // 审计字段 serviceRequest.setTenantId(SecurityUtils.getLoginUser().getTenantId().intValue()); serviceRequest.setCreateBy(SecurityUtils.getUsername()); - serviceRequest.setCreateTime(new java.util.Date()); - + serviceRequest.setCreateTime(new Date()); + // 保存到数据库 iServiceRequestService.save(serviceRequest); - + // 🎯 新增:创建费用项(ChargeItem) // 查询会诊项目的价格定义 @@ -794,23 +958,23 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { .eq(ChargeItemDefinition::getStatusEnum, 2) // 2=启用 .eq(ChargeItemDefinition::getDeleteFlag, "0") .last("LIMIT 1"); - + ChargeItemDefinition chargeItemDef = chargeItemDefinitionMapper.selectOne(priceWrapper); - + if (chargeItemDef != null) { // 🎯 查询患者的费用性质(account_id) // 从 adm_account 表获取患者的 account_id(费用性质) Long accountId = doctorStationAdviceAppMapper.getEncounterContract(consultationRequest.getEncounterId()) - .stream() - .findFirst() - .map(contract -> contract.getAccountId()) - .orElse(null); - + .stream() + .findFirst() + .map(contract -> contract.getAccountId()) + .orElse(null); + if (accountId == null) { log.warn("未找到患者的费用性质,encounterId: {}", consultationRequest.getEncounterId()); throw new RuntimeException("未找到患者的费用性质信息,无法创建费用项"); } - + ChargeItem chargeItem = new ChargeItem(); chargeItem.setStatusEnum(ChargeItemStatus.DRAFT.getValue()); // 草稿状态 chargeItem.setBusNo(AssignSeqEnum.CHARGE_ITEM_NO.getPrefix().concat(busNo)); @@ -818,7 +982,7 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { chargeItem.setPatientId(consultationRequest.getPatientId()); chargeItem.setContextEnum(ItemType.ACTIVITY.getValue()); // 诊疗类型 chargeItem.setEncounterId(consultationRequest.getEncounterId()); - + // 🎯 设置费用性质(必填字段) chargeItem.setAccountId(accountId); @@ -828,29 +992,29 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { chargeItem.setQuantityValue(BigDecimal.ONE); chargeItem.setQuantityUnit("111"); // 次 chargeItem.setTotalPrice(chargeItemDef.getPrice()); - + // 关联医嘱信息 chargeItem.setServiceTable("wor_service_request"); chargeItem.setServiceId(serviceRequest.getId()); chargeItem.setProductTable("wor_activity_definition"); chargeItem.setProductId(activityDefinitionId); - + // 开立信息 chargeItem.setEntererId(consultationRequest.getRequestingPhysicianId()); chargeItem.setRequestingOrgId(consultationRequest.getDepartmentId()); - chargeItem.setEnteredDate(new java.util.Date()); - + chargeItem.setEnteredDate(new Date()); + // 审计字段 chargeItem.setTenantId(SecurityUtils.getLoginUser().getTenantId()); chargeItem.setCreateBy(SecurityUtils.getUsername()); - chargeItem.setCreateTime(new java.util.Date()); - + chargeItem.setCreateTime(new Date()); + iChargeItemService.save(chargeItem); - + } else { log.warn("未找到会诊项目的价格定义,activityId: {}", activityDefinitionId); } - + // 将医嘱ID保存到会诊申请表 consultationRequest.setOrderId(serviceRequest.getId()); consultationRequestMapper.updateById(consultationRequest); @@ -859,10 +1023,10 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { throw new RuntimeException("保存会诊医嘱失败: " + e.getMessage()); } } - + /** * 更新会诊医嘱 - * + * * @param consultationRequest 会诊申请实体 */ private void updateConsultationServiceRequest(ConsultationRequest consultationRequest) { @@ -871,17 +1035,17 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { log.warn("会诊申请没有关联的医嘱ID,跳过更新"); return; } - + ServiceRequest serviceRequest = iServiceRequestService.getById(consultationRequest.getOrderId()); if (serviceRequest == null) { log.warn("未找到关联的医嘱记录,OrderId: {}", consultationRequest.getOrderId()); return; } - + // 更新医嘱内容 serviceRequest.setContentJson(JSON.toJSONString(consultationRequest)); serviceRequest.setUpdateBy(SecurityUtils.getUsername()); - serviceRequest.setUpdateTime(new java.util.Date()); + serviceRequest.setUpdateTime(new Date()); iServiceRequestService.updateById(serviceRequest); @@ -895,7 +1059,7 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { * 更新门诊医嘱状态 * * @param orderId 医嘱ID - * @param status 新状态 + * @param status 新状态 */ private void updateServiceRequestStatus(Long orderId, Integer status) { try { @@ -912,7 +1076,7 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { serviceRequest.setStatusEnum(status); serviceRequest.setUpdateBy(SecurityUtils.getUsername()); - serviceRequest.setUpdateTime(new java.util.Date()); + serviceRequest.setUpdateTime(new Date()); iServiceRequestService.updateById(serviceRequest); @@ -921,12 +1085,10 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { throw new RuntimeException("更新医嘱状态失败: " + e.getMessage()); } } - + @Override public List getConsultationActivities() { try { - log.info("开始查询会诊项目列表"); - // 查询所有会诊相关的诊疗活动 LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.like(ActivityDefinition::getName, "会诊") @@ -934,16 +1096,12 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { .orderBy(true, true, ActivityDefinition::getSortOrder); List activityList = activityDefinitionMapper.selectList(wrapper); - log.info("查询到 {} 个会诊相关诊疗活动", activityList.size()); - if (activityList.isEmpty()) { - log.warn("未查询到任何会诊项目,请检查数据库配置"); return new ArrayList<>(); } // 转换为DTO并查询价格 List resultList = new ArrayList<>(); - int missingPriceCount = 0; for (ActivityDefinition activity : activityList) { ConsultationActivityDto dto = new ConsultationActivityDto(); dto.setId(activity.getId()); @@ -967,23 +1125,654 @@ public class ConsultationAppServiceImpl implements IConsultationAppService { dto.setPrice(chargeItem.getPrice()); } else { dto.setPrice(java.math.BigDecimal.ZERO); - missingPriceCount++; - log.warn("会诊项目: {} (ID: {}) 未找到价格定义", activity.getName(), activity.getId()); + log.warn("会诊项目: {} 未找到价格定义", activity.getName()); } resultList.add(dto); } - - if (missingPriceCount > 0) { - log.warn("共 {} 个会诊项目缺少价格定义", missingPriceCount); - } - - log.info("会诊项目列表查询成功,共 {} 个项目", resultList.size()); return resultList; } catch (Exception e) { log.error("查询会诊项目列表失败", e); throw new RuntimeException("查询会诊项目列表失败: " + e.getMessage()); } } + + // ==================== 会诊确认相关实现 ==================== + + @Override + public List getPendingConfirmationList() { + try { + // 获取当前登录医生ID + Long currentPhysicianId = SecurityUtils.getLoginUser().getPractitionerId(); + log.info("获取待确认会诊列表,当前医生ID: {}", currentPhysicianId); + + // 🎯 关键修改:查询当前医生个人状态为"待确认"、"已确认"或"已签名"的邀请记录 + // 10=已提交(待确认)、20=已确认(待签名)、30=已签名,排除40=已完成 + LambdaQueryWrapper invitedWrapper = new LambdaQueryWrapper<>(); + invitedWrapper.eq(ConsultationInvited::getInvitedPhysicianId, currentPhysicianId) + .in(ConsultationInvited::getInvitedStatus, + ConsultationStatusEnum.SUBMITTED.getCode(), // 10-待确认 + ConsultationStatusEnum.CONFIRMED.getCode(), // 20-已确认(待签名) + ConsultationStatusEnum.SIGNED.getCode()) // 30-已签名 + .orderByDesc(ConsultationInvited::getCreateTime); + + List invitedList = consultationInvitedMapper.selectList(invitedWrapper); + log.info("查询到待确认/待签名/已签名的邀请记录数量: {}", invitedList.size()); + + if (invitedList.isEmpty()) { + log.warn("当前医生没有待确认/待签名/已签名的会诊申请"); + return new ArrayList<>(); + } + + // 获取所有会诊申请ID + List requestIds = invitedList.stream() + .map(ConsultationInvited::getConsultationRequestId) + .distinct() + .collect(Collectors.toList()); + log.info("会诊申请ID列表: {}", requestIds); + + // 🎯 查询会诊申请详情(白名单:只查询正在进行中的会诊,明确业务范围) + // 查询已提交、已确认、已签名状态的会诊,排除已完成(40) + LambdaQueryWrapper requestWrapper = new LambdaQueryWrapper<>(); + requestWrapper.in(ConsultationRequest::getId, requestIds) + .in(ConsultationRequest::getConsultationStatus, + ConsultationStatusEnum.SUBMITTED.getCode(), // 10-已提交 + ConsultationStatusEnum.CONFIRMED.getCode(), // 20-已确认 + ConsultationStatusEnum.SIGNED.getCode()) // 30-已签名 + .orderByDesc(ConsultationRequest::getConsultationRequestDate); + + List requests = consultationRequestMapper.selectList(requestWrapper); + log.info("查询到符合条件的会诊申请数量: {}", requests.size()); + + if (requests.isEmpty()) { + log.warn("没有符合条件的会诊申请"); + return new ArrayList<>(); + } + + // 转换为DTO并添加当前医生的个人状态 + Map invitedMap = invitedList.stream() + .collect(Collectors.toMap(ConsultationInvited::getConsultationRequestId, v -> v, (v1, v2) -> v1)); + + List resultList = new ArrayList<>(); + for (ConsultationRequest request : requests) { + ConsultationConfirmationDto dto = convertToConfirmationDto(request); + + // 添加当前医生的个人状态 + ConsultationInvited myInvited = invitedMap.get(request.getId()); + if (myInvited != null) { + dto.setMyInvitedStatus(myInvited.getInvitedStatus()); + dto.setMyConfirmTime(myInvited.getConfirmTime()); + dto.setMySignatureTime(myInvited.getSignatureTime()); + } + + resultList.add(dto); + } + + log.info("返回待确认会诊列表数量: {}", resultList.size()); + return resultList; + } catch (Exception e) { + log.error("获取待确认会诊列表失败", e); + throw new RuntimeException("获取待确认会诊列表失败: " + e.getMessage()); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean confirmConsultation(ConsultationConfirmationDto dto) { + try { + // 验证 + if (!StringUtils.hasText(dto.getConsultationId())) { + throw new IllegalArgumentException("会诊申请单号不能为空"); + } + if (!StringUtils.hasText(dto.getConsultationOpinion())) { + throw new IllegalArgumentException("会诊意见不能为空"); + } + + + // 1. 查询会诊申请 + LambdaQueryWrapper requestWrapper = new LambdaQueryWrapper<>(); + requestWrapper.eq(ConsultationRequest::getConsultationId, dto.getConsultationId()); + ConsultationRequest request = consultationRequestMapper.selectOne(requestWrapper); + + if (request == null) { + throw new IllegalArgumentException("会诊申请不存在"); + } + + // 只有已提交状态才能确认 + if (request.getConsultationStatus() != ConsultationStatusEnum.SUBMITTED.getCode()) { + throw new IllegalArgumentException("只有已提交状态的会诊申请才能确认"); + } + + // 2. 获取当前登录医生信息 + Long currentPhysicianId = SecurityUtils.getLoginUser().getPractitionerId(); + String currentPhysicianName = SecurityUtils.getLoginUser().getUser().getNickName(); + String currentDeptName = dto.getConfirmingDeptName(); // 前端传递 + + // 3. 查询当前医生的邀请记录 + LambdaQueryWrapper invitedWrapper = new LambdaQueryWrapper<>(); + invitedWrapper.eq(ConsultationInvited::getConsultationRequestId, request.getId()) + .eq(ConsultationInvited::getInvitedPhysicianId, currentPhysicianId); + ConsultationInvited invited = consultationInvitedMapper.selectOne(invitedWrapper); + + if (invited == null) { + throw new IllegalArgumentException("您不在被邀请的医生列表中"); + } + + if (invited.getInvitedStatus() != null && invited.getInvitedStatus() >= ConsultationStatusEnum.CONFIRMED.getCode()) { + throw new IllegalArgumentException("您已经确认过了,无需重复确认"); + } + + // 4. 更新邀请记录(存储会诊意见) + // 格式:科室-医生:意见内容 + String formattedOpinion = String.format("%s-%s:%s", + currentDeptName, + currentPhysicianName, + dto.getConsultationOpinion()); + + invited.setInvitedStatus(ConsultationStatusEnum.CONFIRMED.getCode()); // 已确认 + invited.setConfirmOpinion(formattedOpinion); + invited.setConfirmTime(new Date()); + consultationInvitedMapper.updateById(invited); + + log.info("医生 {} 确认会诊,意见:{}", currentPhysicianName, formattedOpinion); + + // 5. 更新会诊申请的确认计数 + Integer confirmedCount = (request.getConfirmedCount() == null ? 0 : request.getConfirmedCount()) + 1; + request.setConfirmedCount(confirmedCount); + + // 6. 检查是否所有医生都确认了 + Integer invitedCount = request.getInvitedCount(); + if (invitedCount == null || invitedCount == 0) { + // 如果没有初始化,查询实际邀请人数 + LambdaQueryWrapper countWrapper = new LambdaQueryWrapper<>(); + countWrapper.eq(ConsultationInvited::getConsultationRequestId, request.getId()); + invitedCount = consultationInvitedMapper.selectCount(countWrapper).intValue(); + request.setInvitedCount(invitedCount); + } + + log.info("会诊确认进度:{}/{}", confirmedCount, invitedCount); + // 7. 如果所有确认医生数量>= 邀请医生的数量,更新会诊申请状态为已确认 + if (confirmedCount >= invitedCount) { + // 所有医生都确认了 + request.setConsultationStatus(ConsultationStatusEnum.CONFIRMED.getCode()); + + // 创建确认记录 + createConfirmationRecord(request); + + // 更新医嘱状态为"已执行" + updateServiceRequestStatus(request.getOrderId(), RequestStatus.ACTIVE.getValue()); + + log.info("所有医生都已确认,会诊申请状态更新为:已确认"); + } + + consultationRequestMapper.updateById(request); + return true; + } catch (Exception e) { + log.error("确认会诊失败", e); + throw new RuntimeException("确认会诊失败: " + e.getMessage()); + } + } + + /** + * 创建确认记录 + * + * 业务逻辑说明: + * 1. 只有当所有医生都确认后,才会调用此方法 + * 2. "确认医生"应该是最后一个确认的医生(触发全局状态变为"已确认"的医生) + * 3. 也就是当前正在操作确认的医生(从SecurityUtils获取) + */ + private void createConfirmationRecord(ConsultationRequest request) { + try { + // 🎯 获取当前操作的医生信息(最后一个确认的医生) + Long currentPhysicianId = SecurityUtils.getLoginUser().getPractitionerId(); + String currentPhysicianName = SecurityUtils.getLoginUser().getUser().getNickName(); + + // 查询当前医生的邀请记录(获取科室信息) + LambdaQueryWrapper currentWrapper = new LambdaQueryWrapper<>(); + currentWrapper.eq(ConsultationInvited::getConsultationRequestId, request.getId()) + .eq(ConsultationInvited::getInvitedPhysicianId, currentPhysicianId); + ConsultationInvited currentInvited = consultationInvitedMapper.selectOne(currentWrapper); + + if (currentInvited == null) { + log.error("未找到当前医生的邀请记录,医生ID: {}", currentPhysicianId); + return; + } + + // 查询所有已确认的医生(用于汇总意见和医生列表) + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(ConsultationInvited::getConsultationRequestId, request.getId()) + .ge(ConsultationInvited::getInvitedStatus, ConsultationStatusEnum.CONFIRMED.getCode()) + .orderByAsc(ConsultationInvited::getConfirmTime); // 按确认时间排序 + List invitedList = consultationInvitedMapper.selectList(wrapper); + + if (invitedList.isEmpty()) { + log.warn("没有找到已确认的医生"); + return; + } + + // 汇总所有医生的意见 + String allOpinions = invitedList.stream() + .filter(inv -> StringUtils.hasText(inv.getConfirmOpinion())) + .map(ConsultationInvited::getConfirmOpinion) + .collect(Collectors.joining("\n")); + + // 构建医生列表JSON(按确认时间排序) + List> physicians = invitedList.stream() + .map(inv -> { + Map map = new HashMap<>(); + map.put("physicianId", inv.getInvitedPhysicianId()); + map.put("physicianName", inv.getInvitedPhysicianName()); + map.put("deptName", inv.getInvitedDepartmentName()); + map.put("confirmTime", inv.getConfirmTime()); + return map; + }) + .collect(Collectors.toList()); + + // 检查是否已存在确认记录 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(ConsultationConfirmation::getConsultationRequestId, request.getId()); + ConsultationConfirmation existingConfirmation = consultationConfirmationMapper.selectOne(queryWrapper); + + if (existingConfirmation != null) { + // 🎯 更新现有记录(使用当前操作的医生信息) + existingConfirmation.setAllConfirmedDate(new Date()); + existingConfirmation.setConfirmingPhysicianId(currentPhysicianId); + existingConfirmation.setConfirmingPhysicianName(currentPhysicianName); + existingConfirmation.setConfirmingDeptId(currentInvited.getInvitedDepartmentId()); + existingConfirmation.setConfirmingDeptName(currentInvited.getInvitedDepartmentName()); + existingConfirmation.setConfirmingDate(currentInvited.getConfirmTime()); + existingConfirmation.setConsultationStatus(ConsultationStatusEnum.CONFIRMED.getCode()); // 使用枚举 + existingConfirmation.setConsultationOpinion(allOpinions); + existingConfirmation.setConfirmingPhysicians(JSON.toJSONString(physicians)); + + consultationConfirmationMapper.updateById(existingConfirmation); + log.info("更新会诊确认记录成功,确认医生:{},参与医生数:{}", currentPhysicianName, physicians.size()); + } else { + // 🎯 创建新记录(使用当前操作的医生信息) + ConsultationConfirmation confirmation = new ConsultationConfirmation(); + confirmation.setConsultationRequestId(request.getId()); + confirmation.setConsultationId(request.getConsultationId()); + confirmation.setAllConfirmedDate(new Date()); + + // 设置确认医生信息(当前操作的医生 = 最后一个确认的医生) + confirmation.setConfirmingPhysicianId(currentPhysicianId); + confirmation.setConfirmingPhysicianName(currentPhysicianName); + confirmation.setConfirmingDeptId(currentInvited.getInvitedDepartmentId()); + confirmation.setConfirmingDeptName(currentInvited.getInvitedDepartmentName()); + confirmation.setConfirmingDate(currentInvited.getConfirmTime()); + + // 设置会诊状态(使用枚举) + confirmation.setConsultationStatus(ConsultationStatusEnum.CONFIRMED.getCode()); + + confirmation.setConsultationOpinion(allOpinions); + confirmation.setConfirmingPhysicians(JSON.toJSONString(physicians)); + confirmation.setTenantId(SecurityUtils.getLoginUser().getTenantId().longValue()); + + consultationConfirmationMapper.insert(confirmation); + log.info("创建会诊确认记录成功,确认医生:{},参与医生数:{}", currentPhysicianName, physicians.size()); + } + } catch (Exception e) { + log.error("创建确认记录失败", e); + throw new RuntimeException("创建确认记录失败: " + e.getMessage()); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean cancelConfirmation(String consultationId) { + try { + if (!StringUtils.hasText(consultationId)) { + throw new IllegalArgumentException("会诊申请单号不能为空"); + } + + // 1. 查询会诊申请 + LambdaQueryWrapper requestWrapper = new LambdaQueryWrapper<>(); + requestWrapper.eq(ConsultationRequest::getConsultationId, consultationId); + ConsultationRequest request = consultationRequestMapper.selectOne(requestWrapper); + + if (request == null) { + throw new IllegalArgumentException("会诊申请不存在"); + } + + + + // 2. 获取当前登录医生信息 + Long currentPhysicianId = SecurityUtils.getLoginUser().getPractitionerId(); + + // 3. 查询当前医生的邀请记录 + LambdaQueryWrapper invitedWrapper = new LambdaQueryWrapper<>(); + invitedWrapper.eq(ConsultationInvited::getConsultationRequestId, request.getId()) + .eq(ConsultationInvited::getInvitedPhysicianId, currentPhysicianId); + ConsultationInvited invited = consultationInvitedMapper.selectOne(invitedWrapper); + + if (invited == null) { + throw new IllegalArgumentException("您不在被邀请的医生列表中"); + } + + if (invited.getInvitedStatus() == null || invited.getInvitedStatus() < ConsultationStatusEnum.CONFIRMED.getCode()) { + throw new IllegalArgumentException("您还未确认,无需取消"); + } + + if (invited.getInvitedStatus() >= ConsultationStatusEnum.SIGNED.getCode()) { + throw new IllegalArgumentException("已签名的确认无法取消"); + } + + // 4. 取消该医生的确认 + invited.setInvitedStatus(ConsultationStatusEnum.SUBMITTED.getCode()); // 改回待确认 + invited.setConfirmOpinion(null); + invited.setConfirmTime(null); + consultationInvitedMapper.updateById(invited); + + // 5. 更新会诊申请的确认计数 + Integer confirmedCount = request.getConfirmedCount() - 1; + if (confirmedCount < 0) confirmedCount = 0; + request.setConfirmedCount(confirmedCount); + + // 6. 如果不是所有医生都确认了,申请表状态改回"已提交",确认表删除确认记录,邀请表实时更新状态。 + if (confirmedCount < request.getInvitedCount()) { + request.setConsultationStatus(ConsultationStatusEnum.SUBMITTED.getCode()); + + // 🎯 物理删除确认记录(因为不是所有医生都确认了,确认记录不应该存在) + int deletedCount = consultationConfirmationMapper.physicalDeleteByRequestId(request.getId()); + if (deletedCount > 0) { + log.info("取消确认后,确认人数({}) < 邀请人数({}),物理删除确认记录", confirmedCount, request.getInvitedCount()); + } + + log.info("取消确认后,会诊申请状态改回:已提交"); + } + + consultationRequestMapper.updateById(request); + + // 更新医嘱状态为已提交 + updateServiceRequestStatus(request.getOrderId(), RequestStatus.ACTIVE.getValue()); + + return true; + } catch (Exception e) { + log.error("取消确认会诊失败", e); + throw new RuntimeException("取消确认会诊失败: " + e.getMessage()); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean signConsultation(String consultationId) { + try { + if (!StringUtils.hasText(consultationId)) { + throw new IllegalArgumentException("会诊申请单号不能为空"); + } + + // 1. 查询会诊申请 + LambdaQueryWrapper requestWrapper = new LambdaQueryWrapper<>(); + requestWrapper.eq(ConsultationRequest::getConsultationId, consultationId); + ConsultationRequest request = consultationRequestMapper.selectOne(requestWrapper); + + if (request == null) { + throw new IllegalArgumentException("会诊申请不存在"); + } + + // 只要会诊已提交(状态>=10),当前医生已确认就可以签名 + if (request.getConsultationStatus() == null || request.getConsultationStatus() < ConsultationStatusEnum.SUBMITTED.getCode()) { + throw new IllegalArgumentException("会诊申请尚未提交,无法签名"); + } + + // 2. 获取当前登录医生信息 + Long currentPhysicianId = SecurityUtils.getLoginUser().getPractitionerId(); + String currentPhysicianName = SecurityUtils.getLoginUser().getUser().getNickName(); + + // 3. 查询当前医生的邀请记录 + LambdaQueryWrapper invitedWrapper = new LambdaQueryWrapper<>(); + invitedWrapper.eq(ConsultationInvited::getConsultationRequestId, request.getId()) + .eq(ConsultationInvited::getInvitedPhysicianId, currentPhysicianId); + ConsultationInvited invited = consultationInvitedMapper.selectOne(invitedWrapper); + + if (invited == null) { + throw new IllegalArgumentException("您不在被邀请的医生列表中"); + } + + if (invited.getInvitedStatus() == null || invited.getInvitedStatus() < ConsultationStatusEnum.CONFIRMED.getCode()) { + throw new IllegalArgumentException("请先确认会诊后再签名"); + } + + if (invited.getInvitedStatus() >= ConsultationStatusEnum.SIGNED.getCode()) { + throw new IllegalArgumentException("您已经签名过了"); + } + + // 4. 更新邀请记录(只改变个人状态) + invited.setInvitedStatus(ConsultationStatusEnum.SIGNED.getCode()); // 已签名 + invited.setSignatureTime(new Date()); + consultationInvitedMapper.updateById(invited); + + log.info("医生 {} 签名成功", currentPhysicianName); + + // 5. 更新会诊申请的签名计数 + Integer signedCount = (request.getSignedCount() == null ? 0 : request.getSignedCount()) + 1; + request.setSignedCount(signedCount); + + // 6. 检查是否所有医生都签名了 + Integer invitedCount = request.getInvitedCount(); + log.info("会诊签名进度:{}/{}", signedCount, invitedCount); + + if (signedCount >= invitedCount) { + // 🎯 所有医生都签名了,整体状态才变为"已签名" + request.setConsultationStatus(ConsultationStatusEnum.SIGNED.getCode()); + request.setSignature(currentPhysicianName); + request.setSignaturePhysicianId(currentPhysicianId); + request.setSignatureDate(new Date()); + + // 更新确认记录(如果不存在则创建) + LambdaQueryWrapper confirmWrapper = new LambdaQueryWrapper<>(); + confirmWrapper.eq(ConsultationConfirmation::getConsultationRequestId, request.getId()); + ConsultationConfirmation confirmation = consultationConfirmationMapper.selectOne(confirmWrapper); + + if (confirmation == null) { + // 如果确认记录不存在,创建一个 + createConfirmationRecord(request); + } + + // 更新确认记录 + updateConfirmationRecord(request); + + // 更新医嘱状态为"已完成" + updateServiceRequestStatus(request.getOrderId(), RequestStatus.COMPLETED.getValue()); + + log.info("所有医生都已签名,会诊申请状态更新为:已签名(30)"); + } else { + // 🎯 关键修改:部分医生签名,整体状态不变(保持为10或20) + // 签名只改变 invited_status,不改变整体状态 + log.info("部分医生已签名({}/{}), 整体状态保持不变: {}", signedCount, invitedCount, request.getConsultationStatus()); + } + + consultationRequestMapper.updateById(request); + return true; + } catch (Exception e) { + log.error("签名会诊失败", e); + throw new RuntimeException("签名会诊失败: " + e.getMessage()); + } + } + + /** + * 更新确认记录(所有医生都签名后调用) + * + * 业务逻辑说明: + * 1. 只有当所有医生都签名后,才会调用此方法 + * 2. "签名医生"应该是最后一个签名的医生(触发全局状态变为"已签名"的医生) + * 3. 也就是当前正在操作签名的医生(从request中获取) + */ + private void updateConfirmationRecord(ConsultationRequest request) { + try { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(ConsultationConfirmation::getConsultationRequestId, request.getId()); + ConsultationConfirmation confirmation = consultationConfirmationMapper.selectOne(wrapper); + + if (confirmation != null) { + confirmation.setAllSignedDate(new Date()); + confirmation.setConsultationStatus(ConsultationStatusEnum.SIGNED.getCode()); // 使用枚举 + + // 🎯 设置签名医生信息(最后一个签名的医生 = 当前操作的医生) + confirmation.setSignature(request.getSignature()); + confirmation.setSignaturePhysicianId(request.getSignaturePhysicianId()); + confirmation.setSignatureDate(request.getSignatureDate()); + + // 更新医生列表JSON(包含签名时间) + LambdaQueryWrapper invitedWrapper = new LambdaQueryWrapper<>(); + invitedWrapper.eq(ConsultationInvited::getConsultationRequestId, request.getId()) + .ge(ConsultationInvited::getInvitedStatus, 1); + List invitedList = consultationInvitedMapper.selectList(invitedWrapper); + + // 更新汇总的会诊意见 + String allOpinions = invitedList.stream() + .filter(inv -> StringUtils.hasText(inv.getConfirmOpinion())) + .map(ConsultationInvited::getConfirmOpinion) + .collect(Collectors.joining("\n")); + confirmation.setConsultationOpinion(allOpinions); + + List> physicians = invitedList.stream() + .map(inv -> { + Map map = new HashMap<>(); + map.put("physicianId", inv.getInvitedPhysicianId()); + map.put("physicianName", inv.getInvitedPhysicianName()); + map.put("deptName", inv.getInvitedDepartmentName()); + map.put("confirmTime", inv.getConfirmTime()); + map.put("signatureTime", inv.getSignatureTime()); + return map; + }) + .collect(Collectors.toList()); + + confirmation.setConfirmingPhysicians(JSON.toJSONString(physicians)); + consultationConfirmationMapper.updateById(confirmation); + + log.info("更新会诊确认记录成功,所有医生都已签名"); + } + } catch (Exception e) { + log.error("更新确认记录失败", e); + throw new RuntimeException("更新确认记录失败: " + e.getMessage()); + } + } + + @Override + public ConsultationConfirmationDto getConfirmationDetail(String consultationId) { + try { + if (!StringUtils.hasText(consultationId)) { + throw new IllegalArgumentException("会诊申请单号不能为空"); + } + + // 查询会诊申请 + LambdaQueryWrapper requestWrapper = new LambdaQueryWrapper<>(); + requestWrapper.eq(ConsultationRequest::getConsultationId, consultationId); + ConsultationRequest request = consultationRequestMapper.selectOne(requestWrapper); + + if (request == null) { + throw new IllegalArgumentException("会诊申请不存在"); + } + + // 转换为DTO + return convertToConfirmationDto(request); + } catch (Exception e) { + log.error("获取会诊确认详情失败", e); + throw new RuntimeException("获取会诊确认详情失败: " + e.getMessage()); + } + } + + /** + * 转换为会诊确认DTO + */ + private ConsultationConfirmationDto convertToConfirmationDto(ConsultationRequest request) { + ConsultationConfirmationDto dto = new ConsultationConfirmationDto(); + + // 基本信息 + dto.setConsultationRequestId(request.getId()); + dto.setConsultationId(request.getConsultationId()); + dto.setConsultationStatus(request.getConsultationStatus()); + + // 患者信息 + dto.setPatientName(request.getPatientName()); + dto.setGenderText(request.getGenderEnum() != null && request.getGenderEnum() == 1 ? "男" : "女"); + dto.setAge(request.getAge()); + dto.setPatientIdentifierNo(request.getPatientIdentifierNo()); + + // 申请信息 + dto.setApplyDept(request.getDepartment()); + dto.setApplyDoctor(request.getRequestingPhysician()); + dto.setApplyTime(request.getConsultationRequestDate()); + + // 会诊信息 + dto.setConsultationDate(request.getConsultationDate()); + dto.setUrgent("2".equals(request.getConsultationUrgency())); + dto.setInvitedObject(request.getInvitedObject()); + dto.setConsultationPurpose(request.getConsultationPurpose()); + dto.setProvisionalDiagnosis(request.getProvisionalDiagnosis()); + + // 提交信息 + dto.setSubmittingPhysician(request.getConfirmingPhysician()); + dto.setSubmittingTime(request.getConfirmingDate()); + + // 查询确认记录 + LambdaQueryWrapper confirmWrapper = new LambdaQueryWrapper<>(); + confirmWrapper.eq(ConsultationConfirmation::getConsultationRequestId, request.getId()); + ConsultationConfirmation confirmation = consultationConfirmationMapper.selectOne(confirmWrapper); + + if (confirmation != null) { + dto.setId(confirmation.getId()); + dto.setConfirmingPhysicianId(confirmation.getConfirmingPhysicianId()); + dto.setConfirmingPhysicianName(confirmation.getConfirmingPhysicianName()); + dto.setConfirmingDeptId(confirmation.getConfirmingDeptId()); + dto.setConfirmingDeptName(confirmation.getConfirmingDeptName()); + dto.setConfirmingDate(confirmation.getConfirmingDate()); + dto.setConsultationOpinion(confirmation.getConsultationOpinion()); + dto.setConfirmingPhysician(confirmation.getConfirmingPhysicians()); + dto.setSignature(confirmation.getSignature()); + dto.setSignaturePhysicianId(confirmation.getSignaturePhysicianId()); + dto.setSignatureDate(confirmation.getSignatureDate()); + } + + return dto; + } + + @Override + public List getConsultationOpinions(String consultationId) { + try { + if (!StringUtils.hasText(consultationId)) { + return new ArrayList<>(); + } + + // 1. 查询会诊申请 + LambdaQueryWrapper requestWrapper = new LambdaQueryWrapper<>(); + requestWrapper.eq(ConsultationRequest::getConsultationId, consultationId); + ConsultationRequest request = consultationRequestMapper.selectOne(requestWrapper); + + if (request == null) { + return new ArrayList<>(); + } + + // 2. 查询所有已确认的医生及其意见 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(ConsultationInvited::getConsultationRequestId, request.getId()) + .ge(ConsultationInvited::getInvitedStatus, ConsultationStatusEnum.CONFIRMED.getCode()) // 已确认或已签名 + .orderByAsc(ConsultationInvited::getConfirmTime); + + List invitedList = consultationInvitedMapper.selectList(wrapper); + + // 3. 转换为DTO + return invitedList.stream() + .filter(inv -> StringUtils.hasText(inv.getConfirmOpinion())) + .map(inv -> { + ConsultationOpinionDto dto = new ConsultationOpinionDto(); + dto.setPhysicianId(inv.getInvitedPhysicianId()); + dto.setPhysicianName(inv.getInvitedPhysicianName()); + dto.setDeptId(inv.getInvitedDepartmentId()); + dto.setDeptName(inv.getInvitedDepartmentName()); + dto.setOpinion(inv.getConfirmOpinion()); // 已包含"科室-医生:"前缀 + dto.setConfirmTime(inv.getConfirmTime()); + dto.setSignatureTime(inv.getSignatureTime()); + dto.setStatus(inv.getInvitedStatus()); + dto.setIsSigned(inv.getInvitedStatus() != null && inv.getInvitedStatus() >= ConsultationStatusEnum.SIGNED.getCode()); + return dto; + }) + .collect(Collectors.toList()); + } catch (Exception e) { + log.error("获取会诊意见失败", e); + return new ArrayList<>(); + } + } } diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/controller/ConsultationController.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/controller/ConsultationController.java index 97e718ac..fc8fcd8a 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/controller/ConsultationController.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/controller/ConsultationController.java @@ -3,6 +3,8 @@ package com.openhis.web.consultation.controller; import com.core.common.core.domain.R; import com.openhis.web.consultation.appservice.IConsultationAppService; import com.openhis.web.consultation.dto.ConsultationActivityDto; +import com.openhis.web.consultation.dto.ConsultationConfirmationDto; +import com.openhis.web.consultation.dto.ConsultationOpinionDto; import com.openhis.web.consultation.dto.ConsultationRequestDto; import com.openhis.web.consultation.dto.DepartmentTreeDto; import io.swagger.annotations.Api; @@ -49,6 +51,41 @@ public class ConsultationController { } } + /** + * 查询会诊申请列表(支持多条件查询) + */ + @ApiOperation("查询会诊申请列表") + @PostMapping("/query") + public R> queryConsultationList(@RequestBody ConsultationRequestDto dto) { + try { + List list = consultationAppService.queryConsultationList(dto); + return R.ok(list); + } catch (Exception e) { + log.error("查询会诊申请列表失败", e); + return R.fail("查询会诊申请列表失败: " + e.getMessage()); + } + } + + /** + * 分页查询会诊申请列表(支持多条件查询) + */ + @ApiOperation("分页查询会诊申请列表") + @PostMapping("/queryPage") + public R> queryConsultationListPage( + @RequestBody ConsultationRequestDto dto, + @ApiParam("页码") @RequestParam(required = false, defaultValue = "1") Integer pageNum, + @ApiParam("每页大小") @RequestParam(required = false, defaultValue = "10") Integer pageSize) { + try { + log.info("分页查询会诊申请列表,pageNum: {}, pageSize: {}", pageNum, pageSize); + com.baomidou.mybatisplus.extension.plugins.pagination.Page page = + consultationAppService.queryConsultationListPage(dto, pageNum, pageSize); + return R.ok(page); + } catch (Exception e) { + log.error("分页查询会诊申请列表失败", e); + return R.fail("分页查询会诊申请列表失败: " + e.getMessage()); + } + } + /** * 保存会诊申请 */ @@ -173,5 +210,99 @@ public class ConsultationController { return R.fail("获取会诊项目列表失败: " + e.getMessage()); } } + + // ==================== 会诊确认相关接口 ==================== + + /** + * 获取待确认的会诊列表(当前医生被邀请的会诊) + */ + @ApiOperation("获取待确认的会诊列表") + @GetMapping("/confirmation/pending") + public R> getPendingConfirmationList() { + try { + List list = consultationAppService.getPendingConfirmationList(); + return R.ok(list); + } catch (Exception e) { + log.error("获取待确认会诊列表失败", e); + return R.fail("获取待确认会诊列表失败: " + e.getMessage()); + } + } + + /** + * 确认会诊 + */ + @ApiOperation("确认会诊") + @PostMapping("/confirmation/confirm") + public R confirmConsultation(@RequestBody ConsultationConfirmationDto dto) { + try { + Boolean result = consultationAppService.confirmConsultation(dto); + return result ? R.ok("确认成功") : R.fail("确认失败"); + } catch (Exception e) { + log.error("确认会诊失败", e); + return R.fail("确认会诊失败: " + e.getMessage()); + } + } + + /** + * 取消确认会诊 + */ + @ApiOperation("取消确认会诊") + @PostMapping("/confirmation/cancelConfirm") + public R cancelConfirmation(@ApiParam("会诊申请单号") @RequestParam String consultationId) { + try { + Boolean result = consultationAppService.cancelConfirmation(consultationId); + return result ? R.ok("取消确认成功") : R.fail("取消确认失败"); + } catch (Exception e) { + log.error("取消确认会诊失败", e); + return R.fail("取消确认会诊失败: " + e.getMessage()); + } + } + + /** + * 签名会诊 + */ + @ApiOperation("签名会诊") + @PostMapping("/confirmation/sign") + public R signConsultation(@ApiParam("会诊申请单号") @RequestParam String consultationId) { + try { + Boolean result = consultationAppService.signConsultation(consultationId); + return result ? R.ok("签名成功") : R.fail("签名失败"); + } catch (Exception e) { + log.error("签名会诊失败", e); + return R.fail("签名会诊失败: " + e.getMessage()); + } + } + + /** + * 获取会诊确认详情 + */ + @ApiOperation("获取会诊确认详情") + @GetMapping("/confirmation/detail") + public R getConfirmationDetail( + @ApiParam("会诊申请单号") @RequestParam String consultationId) { + try { + ConsultationConfirmationDto detail = consultationAppService.getConfirmationDetail(consultationId); + return R.ok(detail); + } catch (Exception e) { + log.error("获取会诊确认详情失败", e); + return R.fail("获取会诊确认详情失败: " + e.getMessage()); + } + } + + /** + * 获取会诊意见列表 + */ + @ApiOperation("获取会诊意见列表") + @GetMapping("/confirmation/opinions") + public R> getConsultationOpinions( + @ApiParam("会诊申请单号") @RequestParam String consultationId) { + try { + List opinions = consultationAppService.getConsultationOpinions(consultationId); + return R.ok(opinions); + } catch (Exception e) { + log.error("获取会诊意见列表失败", e); + return R.fail("获取会诊意见列表失败: " + e.getMessage()); + } + } } diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/domain/ConsultationConfirmation.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/domain/ConsultationConfirmation.java new file mode 100644 index 00000000..054c6b06 --- /dev/null +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/domain/ConsultationConfirmation.java @@ -0,0 +1,167 @@ +package com.openhis.web.consultation.domain; + +import com.baomidou.mybatisplus.annotation.*; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 会诊确认表实体类 + * + * @author system + * @date 2026-02-06 + */ +@Data +@TableName("consultation_confirmation") +public class ConsultationConfirmation implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private Long id; + + /** + * 会诊申请ID(外键:consultation_request.id) + */ + @TableField("consultation_request_id") + private Long consultationRequestId; + + /** + * 会诊申请单号 + */ + @TableField("consultation_id") + private String consultationId; + + /** + * 确认会诊的医生ID(操作【确认】按钮的当前医生ID) + */ + @TableField("confirming_physician_id") + private Long confirmingPhysicianId; + + /** + * 确认会诊的医生姓名(操作【确认】按钮的当前医生姓名) + */ + @TableField("confirming_physician_name") + private String confirmingPhysicianName; + + /** + * 代表科室ID(操作【确认】按钮的当前开单科室ID) + */ + @TableField("confirming_dept_id") + private Long confirmingDeptId; + + /** + * 代表科室名称(操作【确认】按钮的当前开单科室) + */ + @TableField("confirming_dept_name") + private String confirmingDeptName; + + /** + * 确认会诊的日期(操作【确认】按钮当前系统时间) + */ + @TableField("confirming_date") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date confirmingDate; + + /** + * 会诊状态:0-取消确认(作废),20-已确认(会诊医生已查看/同意,可写初步意见),30-已签名(已电子签名,意见最终生效),40-已完成(会诊报告已回写,流程关闭) + */ + @TableField("consultation_status") + private Integer consultationStatus; + + /** + * 会诊意见(最多500字) + */ + @TableField("consultation_opinion") + private String consultationOpinion; + + /** + * 会诊确认参加医师(多个医师信息,JSON格式) + */ + @TableField("confirming_physicians") + private String confirmingPhysicians; + + /** + * 所有医生都确认的时间 + */ + @TableField("all_confirmed_date") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date allConfirmedDate; + + /** + * 所有医生都签名的时间 + */ + @TableField("all_signed_date") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date allSignedDate; + + /** + * 签名医生姓名 + */ + @TableField("signature") + private String signature; + + /** + * 签名医生ID + */ + @TableField("signature_physician_id") + private Long signaturePhysicianId; + + /** + * 签名时间 + */ + @TableField("signature_date") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date signatureDate; + + /** + * 创建时间 + */ + @TableField(value = "create_time", fill = FieldFill.INSERT) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** + * 更新时间(由 Java 代码维护) + */ + @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + /** + * 创建人 + */ + @TableField(value = "create_by", fill = FieldFill.INSERT) + private String createBy; + + /** + * 更新人 + */ + @TableField(value = "update_by", fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + /** + * 租户ID + */ + @TableField("tenant_id") + private Long tenantId; + + /** + * 逻辑删除标识:0-未删除,1-已删除 + */ + @TableField("is_deleted") + @TableLogic + private Integer isDeleted; + + /** + * 备注 + */ + @TableField("remark") + private String remark; +} + diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/domain/ConsultationInvited.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/domain/ConsultationInvited.java index d3739461..c1d7402d 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/domain/ConsultationInvited.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/domain/ConsultationInvited.java @@ -56,7 +56,13 @@ public class ConsultationInvited implements Serializable { private String invitedPhysicianName; /** - * 邀请状态(0-待确认,1-已确认,2-已拒绝) + * 邀请状态(使用ConsultationStatusEnum) + * 0-新开(待确认) + * 10-已提交(待确认) + * 20-已确认 + * 30-已签名 + * 40-已完成 + * 50-已取消(已拒绝) */ @TableField("invited_status") private Integer invitedStatus; @@ -69,11 +75,18 @@ public class ConsultationInvited implements Serializable { private Date confirmTime; /** - * 确认意见 + * 确认意见(格式:科室-医生:意见内容) */ @TableField("confirm_opinion") private String confirmOpinion; + /** + * 签名时间 + */ + @TableField("signature_time") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date signatureTime; + /** * 创建时间 */ diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/domain/ConsultationRequest.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/domain/ConsultationRequest.java index 310677cc..74aa3b18 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/domain/ConsultationRequest.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/domain/ConsultationRequest.java @@ -218,6 +218,24 @@ public class ConsultationRequest implements Serializable { @TableField("cancel_reason") private String cancelReason; + /** + * 已确认的医生数量 + */ + @TableField("confirmed_count") + private Integer confirmedCount; + + /** + * 已签名的医生数量 + */ + @TableField("signed_count") + private Integer signedCount; + + /** + * 被邀请的医生总数 + */ + @TableField("invited_count") + private Integer invitedCount; + /** * 创建时间 */ diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/dto/ConsultationConfirmationDto.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/dto/ConsultationConfirmationDto.java new file mode 100644 index 00000000..1550db04 --- /dev/null +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/dto/ConsultationConfirmationDto.java @@ -0,0 +1,132 @@ +package com.openhis.web.consultation.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 会诊确认DTO + * + * @author system + * @date 2026-02-06 + */ +@Data +@ApiModel("会诊确认DTO") +public class ConsultationConfirmationDto implements Serializable { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty("主键ID") + private Long id; + + @ApiModelProperty("会诊申请ID") + private Long consultationRequestId; + + @ApiModelProperty("会诊申请单号") + private String consultationId; + + @ApiModelProperty("确认医生ID") + private Long confirmingPhysicianId; + + @ApiModelProperty("确认医生姓名") + private String confirmingPhysicianName; + + @ApiModelProperty("代表科室ID") + private Long confirmingDeptId; + + @ApiModelProperty("代表科室名称") + private String confirmingDeptName; + + @ApiModelProperty("确认日期") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date confirmingDate; + + @ApiModelProperty("会诊状态:0-取消确认,20-已确认,30-已签名,40-已完成") + private Integer consultationStatus; + + @ApiModelProperty("会诊意见") + private String consultationOpinion; + + @ApiModelProperty("会诊确认参加医师") + private String confirmingPhysician; + + @ApiModelProperty("签名医生姓名") + private String signature; + + @ApiModelProperty("签名医生ID") + private Long signaturePhysicianId; + + @ApiModelProperty("签名时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date signatureDate; + + @ApiModelProperty("备注") + private String remark; + + // ========== 扩展字段(用于前端显示) ========== + + @ApiModelProperty("患者姓名") + private String patientName; + + @ApiModelProperty("性别") + private String genderText; + + @ApiModelProperty("年龄") + private Integer age; + + @ApiModelProperty("就诊卡号") + private String patientIdentifierNo; + + @ApiModelProperty("申请科室") + private String applyDept; + + @ApiModelProperty("申请医师") + private String applyDoctor; + + @ApiModelProperty("申请时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date applyTime; + + @ApiModelProperty("会诊时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date consultationDate; + + @ApiModelProperty("紧急标志") + private Boolean urgent; + + @ApiModelProperty("会诊邀请对象") + private String invitedObject; + + @ApiModelProperty("病史及目的") + private String consultationPurpose; + + @ApiModelProperty("门诊诊断") + private String provisionalDiagnosis; + + @ApiModelProperty("提交医生") + private String submittingPhysician; + + @ApiModelProperty("提交时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date submittingTime; + + // ========== 当前医生的个人状态 ========== + + @ApiModelProperty("当前医生的邀请状态:0-待确认,1-已确认,2-已拒绝,3-已签名") + private Integer myInvitedStatus; + + @ApiModelProperty("当前医生的确认时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date myConfirmTime; + + @ApiModelProperty("当前医生的签名时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date mySignatureTime; +} + + + diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/dto/ConsultationOpinionDto.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/dto/ConsultationOpinionDto.java new file mode 100644 index 00000000..6c41c980 --- /dev/null +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/dto/ConsultationOpinionDto.java @@ -0,0 +1,52 @@ +package com.openhis.web.consultation.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 会诊意见DTO + * + * @author system + * @date 2026-02-09 + */ +@Data +@ApiModel("会诊意见DTO") +public class ConsultationOpinionDto implements Serializable { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty("医生ID") + private Long physicianId; + + @ApiModelProperty("医生姓名") + private String physicianName; + + @ApiModelProperty("科室ID") + private Long deptId; + + @ApiModelProperty("科室名称") + private String deptName; + + @ApiModelProperty("会诊意见(已包含科室-医生前缀)") + private String opinion; + + @ApiModelProperty("确认时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date confirmTime; + + @ApiModelProperty("签名时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date signatureTime; + + @ApiModelProperty("状态:0-待确认,1-已确认,2-已拒绝,3-已签名") + private Integer status; + + @ApiModelProperty("是否已签名") + private Boolean isSigned; +} + diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/dto/ConsultationRequestDto.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/dto/ConsultationRequestDto.java index faab88cf..c292c3d1 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/dto/ConsultationRequestDto.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/dto/ConsultationRequestDto.java @@ -105,7 +105,7 @@ public class ConsultationRequestDto implements Serializable { */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date consultationDate; - + /** * 申请时间 */ @@ -132,7 +132,7 @@ public class ConsultationRequestDto implements Serializable { * 会诊项目名称(如:院内会诊、远程会诊等) */ private String consultationActivityName; - + /** * 紧急程度(1=普通,2=紧急) */ @@ -185,6 +185,34 @@ public class ConsultationRequestDto implements Serializable { */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date myConfirmTime; + + // ==================== 会诊记录相关字段(会诊完成后显示) ==================== + + /** + * 会诊邀请参加医师(已签名医生列表,格式:科室-姓名、科室-姓名) + */ + private String invitedPhysiciansText; + + /** + * 所属医生(第一个签名的医生) + */ + private String attendingPhysician; + + /** + * 代表科室(第一个签名医生的科室) + */ + private String representDepartment; + + /** + * 签名医生(第一个签名的医生) + */ + private String signPhysician; + + /** + * 签名时间(第一个签名医生的签名时间) + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date signTime; } diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/dto/InvitedObjectDto.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/dto/InvitedObjectDto.java index 29fd3c6a..918dadc3 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/dto/InvitedObjectDto.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/dto/InvitedObjectDto.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import lombok.Data; import java.io.Serializable; +import java.util.Date; /** * 邀请对象DTO @@ -38,6 +39,20 @@ public class InvitedObjectDto implements Serializable { * 医生姓名 */ private String physicianName; + + /** + * 邀请状态:0=待确认,1=已确认,3=已签名 + */ + private Integer invitedStatus; + + /** + * 确认时间 + */ + private Date confirmTime; + + /** + * 签名时间 + */ + private Date signatureTime; } - diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/enums/ConsultationStatusEnum.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/enums/ConsultationStatusEnum.java index 981b7398..7479ca66 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/enums/ConsultationStatusEnum.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/enums/ConsultationStatusEnum.java @@ -72,7 +72,7 @@ public enum ConsultationStatusEnum { * 判断是否可以编辑 */ public boolean canEdit() { - return this == NEW || this == SUBMITTED; + return this == NEW; } /** @@ -83,3 +83,6 @@ public enum ConsultationStatusEnum { } } + + + diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/enums/ConsultationUrgencyEnum.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/enums/ConsultationUrgencyEnum.java index 65df3c9b..a18ce3cb 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/enums/ConsultationUrgencyEnum.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/enums/ConsultationUrgencyEnum.java @@ -49,3 +49,8 @@ public enum ConsultationUrgencyEnum { } } + + + + + diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/enums/InvitedStatusEnum.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/enums/InvitedStatusEnum.java index eeada950..349c0f69 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/enums/InvitedStatusEnum.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/enums/InvitedStatusEnum.java @@ -54,3 +54,7 @@ public enum InvitedStatusEnum { } } + + + + diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/mapper/ConsultationConfirmationMapper.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/mapper/ConsultationConfirmationMapper.java new file mode 100644 index 00000000..49ea8d56 --- /dev/null +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/mapper/ConsultationConfirmationMapper.java @@ -0,0 +1,29 @@ +package com.openhis.web.consultation.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.openhis.web.consultation.domain.ConsultationConfirmation; +import org.apache.ibatis.annotations.Delete; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +/** + * 会诊确认Mapper接口 + * + * @author system + * @date 2026-02-06 + */ +@Mapper +public interface ConsultationConfirmationMapper extends BaseMapper { + + /** + * 物理删除确认记录(根据会诊申请ID) + * + * @param consultationRequestId 会诊申请ID + * @return 删除的记录数 + */ + @Delete("DELETE FROM consultation_confirmation WHERE consultation_request_id = #{consultationRequestId}") + int physicalDeleteByRequestId(@Param("consultationRequestId") Long consultationRequestId); +} + + + diff --git a/openhis-server-new/openhis-application/src/main/resources/sql/consultation_confirmation_fix.sql b/openhis-server-new/openhis-application/src/main/resources/sql/consultation_confirmation_fix.sql new file mode 100644 index 00000000..e4010c9f --- /dev/null +++ b/openhis-server-new/openhis-application/src/main/resources/sql/consultation_confirmation_fix.sql @@ -0,0 +1,27 @@ +-- ============================================ +-- 修复会诊确认表的 NOT NULL 约束问题 +-- ============================================ + +-- 将 confirming_physician_id 等字段改为可空 +-- 因为在新的方案中,这些字段在所有医生确认后才会填充 + +ALTER TABLE hisdev.consultation_confirmation +ALTER COLUMN confirming_physician_id DROP NOT NULL; + +ALTER TABLE hisdev.consultation_confirmation +ALTER COLUMN confirming_physician_name DROP NOT NULL; + +ALTER TABLE hisdev.consultation_confirmation +ALTER COLUMN confirming_dept_id DROP NOT NULL; + +ALTER TABLE hisdev.consultation_confirmation +ALTER COLUMN confirming_dept_name DROP NOT NULL; + +ALTER TABLE hisdev.consultation_confirmation +ALTER COLUMN confirming_date DROP NOT NULL; + +-- 说明: +-- 在新的方案中,consultation_confirmation 记录在所有医生确认后才创建 +-- 这些字段用于记录"最后一个确认的医生"或"代表性医生" +-- 因此允许为 NULL,实际的医生信息存储在 confirming_physicians (JSON) 字段中 + diff --git a/openhis-server-new/openhis-application/src/main/resources/sql/consultation_confirmation_optimization.sql b/openhis-server-new/openhis-application/src/main/resources/sql/consultation_confirmation_optimization.sql new file mode 100644 index 00000000..1e38b57e --- /dev/null +++ b/openhis-server-new/openhis-application/src/main/resources/sql/consultation_confirmation_optimization.sql @@ -0,0 +1,94 @@ +-- ============================================ +-- 会诊确认功能优化 - 数据库表结构修改 (PostgreSQL版本) +-- ============================================ + +-- 1. 修改 consultation_request 表(添加计数字段) +ALTER TABLE hisdev.consultation_request +ADD COLUMN IF NOT EXISTS confirmed_count INT DEFAULT 0; + +ALTER TABLE hisdev.consultation_request +ADD COLUMN IF NOT EXISTS signed_count INT DEFAULT 0; + +ALTER TABLE hisdev.consultation_request +ADD COLUMN IF NOT EXISTS invited_count INT DEFAULT 0; + +-- 2. 修改 consultation_invited 表(添加签名时间字段) +ALTER TABLE hisdev.consultation_invited +ADD COLUMN IF NOT EXISTS signature_time TIMESTAMP NULL; + +-- 3. 修改 consultation_confirmation 表(优化字段) +ALTER TABLE hisdev.consultation_confirmation +ADD COLUMN IF NOT EXISTS all_confirmed_date TIMESTAMP NULL; + +ALTER TABLE hisdev.consultation_confirmation +ADD COLUMN IF NOT EXISTS all_signed_date TIMESTAMP NULL; + +-- 检查 confirming_physicians 字段是否存在,如果不存在则添加 +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'hisdev' + AND table_name = 'consultation_confirmation' + AND column_name = 'confirming_physicians' + ) THEN + -- 如果 confirming_physician 存在,则重命名 + IF EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_schema = 'hisdev' + AND table_name = 'consultation_confirmation' + AND column_name = 'confirming_physician' + ) THEN + ALTER TABLE hisdev.consultation_confirmation + RENAME COLUMN confirming_physician TO confirming_physicians; + ELSE + -- 如果都不存在,则新增 + ALTER TABLE hisdev.consultation_confirmation + ADD COLUMN confirming_physicians TEXT; + END IF; + END IF; +END $$; + +-- 4. 为现有数据初始化计数字段 +UPDATE hisdev.consultation_request cr +SET invited_count = ( + SELECT COUNT(*) + FROM hisdev.consultation_invited ci + WHERE ci.consultation_request_id = cr.id + AND ci.is_deleted = 0 +) +WHERE cr.invited_count = 0 OR cr.invited_count IS NULL; + +UPDATE hisdev.consultation_request cr +SET confirmed_count = ( + SELECT COUNT(*) + FROM hisdev.consultation_invited ci + WHERE ci.consultation_request_id = cr.id + AND ci.invited_status >= 1 + AND ci.is_deleted = 0 +) +WHERE cr.confirmed_count = 0 OR cr.confirmed_count IS NULL; + +UPDATE hisdev.consultation_request cr +SET signed_count = ( + SELECT COUNT(*) + FROM hisdev.consultation_invited ci + WHERE ci.consultation_request_id = cr.id + AND ci.invited_status >= 3 + AND ci.is_deleted = 0 +) +WHERE cr.signed_count = 0 OR cr.signed_count IS NULL; + +-- 5. 添加索引优化查询性能(如果不存在) +CREATE INDEX IF NOT EXISTS idx_invited_status ON hisdev.consultation_invited(invited_status); +CREATE INDEX IF NOT EXISTS idx_confirm_time ON hisdev.consultation_invited(confirm_time); + +-- 6. 添加字段注释 +COMMENT ON COLUMN hisdev.consultation_request.confirmed_count IS '已确认的医生数量'; +COMMENT ON COLUMN hisdev.consultation_request.signed_count IS '已签名的医生数量'; +COMMENT ON COLUMN hisdev.consultation_request.invited_count IS '被邀请的医生总数'; +COMMENT ON COLUMN hisdev.consultation_invited.signature_time IS '签名时间'; +COMMENT ON COLUMN hisdev.consultation_confirmation.all_confirmed_date IS '所有医生都确认的时间'; +COMMENT ON COLUMN hisdev.consultation_confirmation.all_signed_date IS '所有医生都签名的时间'; +COMMENT ON COLUMN hisdev.consultation_confirmation.confirming_physicians IS '参与确认的医生列表(JSON格式)'; + diff --git a/openhis-ui-vue3/src/layout/components/Sidebar/SidebarItem.vue b/openhis-ui-vue3/src/layout/components/Sidebar/SidebarItem.vue index 6637f67f..9bd3b986 100644 --- a/openhis-ui-vue3/src/layout/components/Sidebar/SidebarItem.vue +++ b/openhis-ui-vue3/src/layout/components/Sidebar/SidebarItem.vue @@ -86,6 +86,19 @@ function resolvePath(routePath, routeQuery) { return props.basePath } if (routeQuery) { + try { + // 检查 routeQuery 是否已经是对象 + if (typeof routeQuery === 'object') { + return { path: getNormalPath(props.basePath + '/' + routePath), query: routeQuery } + } + // 尝试解析 JSON 字符串 + let query = JSON.parse(routeQuery); + return { path: getNormalPath(props.basePath + '/' + routePath), query: query } + } catch (e) { + // 如果解析失败,将其作为普通字符串处理 + console.warn('Failed to parse routeQuery as JSON:', routeQuery, e); + return getNormalPath(props.basePath + '/' + routePath) + } try { let query = JSON.parse(routeQuery); return { path: getNormalPath(props.basePath + '/' + routePath), query: query } @@ -93,6 +106,19 @@ function resolvePath(routePath, routeQuery) { console.error('Failed to parse routeQuery:', routeQuery, e); return getNormalPath(props.basePath + '/' + routePath) } + try { + // 检查 routeQuery 是否已经是对象 + if (typeof routeQuery === 'object') { + return { path: getNormalPath(props.basePath + '/' + routePath), query: routeQuery } + } + // 尝试解析 JSON 字符串 + let query = JSON.parse(routeQuery); + return { path: getNormalPath(props.basePath + '/' + routePath), query: query } + } catch (e) { + // 如果解析失败,将其作为普通字符串处理 + console.warn('Failed to parse routeQuery as JSON:', routeQuery, e); + return getNormalPath(props.basePath + '/' + routePath) + } } return getNormalPath(props.basePath + '/' + routePath) } diff --git a/openhis-ui-vue3/src/router/index.js b/openhis-ui-vue3/src/router/index.js index 153ce38b..e518251b 100644 --- a/openhis-ui-vue3/src/router/index.js +++ b/openhis-ui-vue3/src/router/index.js @@ -237,6 +237,21 @@ export const dynamicRoutes = [ }, ], }, + { + path: '/doctorstation', + component: Layout, + redirect: '/doctorstation/index', + name: 'DoctorStation', + meta: { title: '医生工作站', icon: 'operation' }, + children: [ + { + path: 'pending-emr', + component: () => import('@/views/doctorstation/pendingEmr.vue'), + name: 'PendingEmr', + meta: { title: '待写病历', icon: 'document', permissions: ['doctorstation:pending-emr:view'] } + } + ] + }, { path: '/features', component: Layout, @@ -298,6 +313,40 @@ export const dynamicRoutes = [ meta: { title: '门诊日结', icon: 'document' } } ] + }, + { + path: '/consultationmanagement', + component: Layout, + name: 'ConsultationManagement', + meta: { title: '会诊管理', icon: 'operation' }, + children: [ + { + path: 'consultationapplication', + component: () => import('@/views/consultationmanagement/consultationapplication/index.vue'), + name: 'ConsultationApplication', + meta: { title: '门诊会诊申请管理', icon: 'document' } + }, + { + path: 'consultationconfirmation', + component: () => import('@/views/consultationmanagement/consultationconfirmation/index.vue'), + name: 'ConsultationConfirmation', + meta: { title: '门诊会诊申请确认', icon: 'document' } + } + ] + }, + { + path: '/medicationmanagement', + component: Layout, + name: 'MedicationManagement', + meta: { title: '药房管理', icon: 'medication' }, + children: [ + { + path: 'dayEndSettlement', + component: () => import('@/views/medicationmanagement/dayEndSettlement/index.vue'), + name: 'DayEndSettlement', + meta: { title: '日结结算单管理', icon: 'document' } + } + ] } ]; diff --git a/openhis-ui-vue3/src/views/consultationmanagement/consultationapplication/api.js b/openhis-ui-vue3/src/views/consultationmanagement/consultationapplication/api.js new file mode 100644 index 00000000..4270bbeb --- /dev/null +++ b/openhis-ui-vue3/src/views/consultationmanagement/consultationapplication/api.js @@ -0,0 +1,125 @@ +import request from '@/utils/request' + +/** + * 获取会诊申请列表(简单查询) + * @param {Object} params 查询参数 + */ +export function getConsultationList(params) { + return request({ + url: '/consultation/list', + method: 'get', + params + }) +} + +/** + * 查询会诊申请列表(支持多条件查询) + * @param {Object} data 查询条件 + */ +export function queryConsultationList(data) { + return request({ + url: '/consultation/query', + method: 'post', + data + }) +} + +/** + * 分页查询会诊申请列表(支持多条件查询) + * @param {Object} data 查询条件 + * @param {Number} pageNum 页码 + * @param {Number} pageSize 每页大小 + */ +export function queryConsultationListPage(data, pageNum, pageSize) { + return request({ + url: '/consultation/queryPage', + method: 'post', + data, + params: { pageNum, pageSize }, + headers: { + repeatSubmit: false // 关键:告诉后端不要进行防重复提交检查 + } + }) +} + +/** + * 保存会诊申请(新增或修改) + * @param {Object} data 会诊申请数据 + */ +export function saveConsultation(data) { + return request({ + url: '/consultation/save', + method: 'post', + data + }) +} + +/** + * 提交会诊申请 + * @param {String} consultationId 会诊申请单号 + */ +export function submitConsultation(consultationId) { + return request({ + url: '/consultation/submit', + method: 'post', + params: { consultationId } + }) +} + +/** + * 作废会诊申请 + * @param {String} consultationId 会诊申请单号 + * @param {String} cancelReason 作废原因 + */ +export function cancelConsultation(consultationId, cancelReason) { + return request({ + url: '/consultation/cancel', + method: 'post', + params: { consultationId, cancelReason } + }) +} + +/** + * 结束会诊申请 + * @param {String} consultationId 会诊申请单号 + */ +export function completeConsultation(consultationId) { + return request({ + url: '/consultation/complete', + method: 'post', + params: { consultationId } + }) +} + +/** + * 获取科室医生树 + */ +export function getDepartmentTree() { + return request({ + url: '/consultation/departmentTree', + method: 'get' + }) +} + +/** + * 获取主诊断 + * @param {Number} encounterId 就诊ID + */ +export function getMainDiagnosis(encounterId) { + return request({ + url: '/consultation/mainDiagnosis', + method: 'get', + params: { encounterId } + }) +} + +/** + * 获取会诊项目列表 + */ +export function getConsultationActivities() { + return request({ + url: '/consultation/activities', + method: 'get' + }) +} + diff --git a/openhis-ui-vue3/src/views/consultationmanagement/consultationapplication/index.vue b/openhis-ui-vue3/src/views/consultationmanagement/consultationapplication/index.vue new file mode 100644 index 00000000..72b12f42 --- /dev/null +++ b/openhis-ui-vue3/src/views/consultationmanagement/consultationapplication/index.vue @@ -0,0 +1,743 @@ + + + + + + diff --git a/openhis-ui-vue3/src/views/consultationmanagement/consultationconfirmation/api.js b/openhis-ui-vue3/src/views/consultationmanagement/consultationconfirmation/api.js new file mode 100644 index 00000000..5aebbf10 --- /dev/null +++ b/openhis-ui-vue3/src/views/consultationmanagement/consultationconfirmation/api.js @@ -0,0 +1,74 @@ +import request from '@/utils/request' + +/** + * 获取待确认的会诊列表(当前医生被邀请的会诊) + */ +export function getPendingConfirmationList() { + return request({ + url: '/consultation/confirmation/pending', + method: 'get' + }) +} + +/** + * 确认会诊 + * @param {Object} data 会诊确认数据 + */ +export function confirmConsultation(data) { + return request({ + url: '/consultation/confirmation/confirm', + method: 'post', + data + }) +} + +/** + * 取消确认会诊 + * @param {String} consultationId 会诊申请单号 + */ +export function cancelConfirmation(consultationId) { + return request({ + url: '/consultation/confirmation/cancelConfirm', + method: 'post', + params: { consultationId } + }) +} + +/** + * 签名会诊 + * @param {String} consultationId 会诊申请单号 + */ +export function signConsultation(consultationId) { + return request({ + url: '/consultation/confirmation/sign', + method: 'post', + params: { consultationId } + }) +} + +/** + * 获取会诊确认详情 + * @param {String} consultationId 会诊申请单号 + */ +export function getConfirmationDetail(consultationId) { + return request({ + url: '/consultation/confirmation/detail', + method: 'get', + params: { consultationId } + }) +} + +/** + * 获取会诊意见列表 + * @param {String} consultationId 会诊申请单号 + */ +export function getConsultationOpinions(consultationId) { + return request({ + url: '/consultation/confirmation/opinions', + method: 'get', + params: { consultationId } + }) +} + + + diff --git a/openhis-ui-vue3/src/views/consultationmanagement/consultationconfirmation/index.vue b/openhis-ui-vue3/src/views/consultationmanagement/consultationconfirmation/index.vue index 87b0d6a1..efd75027 100644 --- a/openhis-ui-vue3/src/views/consultationmanagement/consultationconfirmation/index.vue +++ b/openhis-ui-vue3/src/views/consultationmanagement/consultationconfirmation/index.vue @@ -30,28 +30,37 @@ stripe highlight-current-row @current-change="handleRowChange" + v-loading="loading" > - - + + + + - + + + @@ -68,7 +77,7 @@ - + @@ -78,7 +87,7 @@ - + @@ -93,7 +102,7 @@ - + @@ -103,52 +112,92 @@ - + - + - + - + - - + + - + + + + + +

+ + + +
+ + {{ opinion.deptName }} + {{ opinion.physicianName }} + + + 已签名 + + 已确认 +
+
+ {{ opinion.opinion }} +
+ +
+
+
+ +
- + - + - + - + @@ -158,94 +207,85 @@ - - - - - - - - - diff --git a/openhis-ui-vue3/src/views/doctorstation/components/consultation.vue b/openhis-ui-vue3/src/views/doctorstation/components/consultation.vue index 13e07043..0afb2ea5 100644 --- a/openhis-ui-vue3/src/views/doctorstation/components/consultation.vue +++ b/openhis-ui-vue3/src/views/doctorstation/components/consultation.vue @@ -8,7 +8,7 @@
结束 - 保存 + 保存
@@ -62,15 +62,19 @@ 取消提交 + +