diff --git a/consultation_invited.sql b/consultation_invited.sql new file mode 100644 index 00000000..39d19667 --- /dev/null +++ b/consultation_invited.sql @@ -0,0 +1,42 @@ +-- 会诊邀请对象表 +CREATE TABLE hisdev.consultation_invited ( + id BIGINT NOT NULL PRIMARY KEY, + consultation_request_id BIGINT NOT NULL, + invited_department_id BIGINT, + invited_department_name VARCHAR(100), + invited_physician_id BIGINT, + invited_physician_name VARCHAR(50), + invited_status SMALLINT DEFAULT 0, + confirm_time TIMESTAMP, + confirm_opinion TEXT, + create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + create_by VARCHAR(64), + update_by VARCHAR(64), + tenant_id BIGINT, + is_deleted SMALLINT DEFAULT 0 +); + +-- 添加注释 +COMMENT ON TABLE hisdev.consultation_invited IS '会诊邀请对象表'; +COMMENT ON COLUMN hisdev.consultation_invited.id IS '主键ID'; +COMMENT ON COLUMN hisdev.consultation_invited.consultation_request_id IS '会诊申请ID(外键)'; +COMMENT ON COLUMN hisdev.consultation_invited.invited_department_id IS '邀请科室ID'; +COMMENT ON COLUMN hisdev.consultation_invited.invited_department_name IS '邀请科室名称'; +COMMENT ON COLUMN hisdev.consultation_invited.invited_physician_id IS '邀请医生ID'; +COMMENT ON COLUMN hisdev.consultation_invited.invited_physician_name IS '邀请医生姓名'; +COMMENT ON COLUMN hisdev.consultation_invited.invited_status IS '邀请状态(0-待确认,1-已确认,2-已拒绝)'; +COMMENT ON COLUMN hisdev.consultation_invited.confirm_time IS '确认时间'; +COMMENT ON COLUMN hisdev.consultation_invited.confirm_opinion IS '确认意见'; +COMMENT ON COLUMN hisdev.consultation_invited.create_time IS '创建时间'; +COMMENT ON COLUMN hisdev.consultation_invited.update_time IS '更新时间'; +COMMENT ON COLUMN hisdev.consultation_invited.create_by IS '创建人'; +COMMENT ON COLUMN hisdev.consultation_invited.update_by IS '更新人'; +COMMENT ON COLUMN hisdev.consultation_invited.tenant_id IS '租户ID'; +COMMENT ON COLUMN hisdev.consultation_invited.is_deleted IS '删除标识(0-未删除,1-已删除)'; + +-- 创建索引 +CREATE INDEX idx_consultation_request_id ON hisdev.consultation_invited(consultation_request_id); +CREATE INDEX idx_invited_physician_id ON hisdev.consultation_invited(invited_physician_id); +CREATE INDEX idx_tenant_id ON hisdev.consultation_invited(tenant_id); + 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 new file mode 100644 index 00000000..ba1719be --- /dev/null +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/appservice/IConsultationAppService.java @@ -0,0 +1,89 @@ +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 java.util.List; +import java.util.Map; + +/** + * 会诊管理AppService接口 + * + * @author system + * @date 2026-01-29 + */ +public interface IConsultationAppService { + + /** + * 获取会诊列表 + * + * @param encounterId 就诊ID(可选,如果为null则查询当前医生的所有会诊申请) + * @return 会诊列表 + */ + List getConsultationList(Long encounterId); + + /** + * 保存会诊申请 + * + * @param dto 会诊申请DTO + * @return 是否成功 + */ + Boolean saveConsultation(ConsultationRequestDto dto); + + /** + * 提交会诊申请 + * + * @param consultationId 会诊申请单号 + * @return 是否成功 + */ + Boolean submitConsultation(String consultationId); + + /** + * 作废会诊申请 + * + * @param consultationId 会诊申请单号 + * @param cancelReason 作废原因 + * @return 是否成功 + */ + Boolean cancelConsultation(String consultationId, String cancelReason); + + /** + * 结束会诊申请 + * + * @param consultationId 会诊申请单号 + * @return 是否成功 + */ + Boolean completeConsultation(String consultationId); + + /** + * 获取科室医生树 + * + * @return 科室医生树 + */ + List getDepartmentTree(); + + /** + * 获取主诊断 + * + * @param encounterId 就诊ID + * @return 主诊断信息 + */ + Map getMainDiagnosis(Long encounterId); + + /** + * 获取我的会诊邀请列表(被邀请的医生查看) + * + * @return 会诊邀请列表 + */ + List getMyInvitations(); + + /** + * 获取所有会诊项目及价格 + * + * @return 会诊项目列表 + */ + List getConsultationActivities(); +} + + 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 new file mode 100644 index 00000000..e7857d5d --- /dev/null +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/appservice/impl/ConsultationAppServiceImpl.java @@ -0,0 +1,967 @@ +package com.openhis.web.consultation.appservice.impl; + +import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.core.common.utils.SecurityUtils; +import com.openhis.administration.domain.Encounter; +import com.openhis.administration.domain.EncounterDiagnosis; +import com.openhis.administration.domain.Organization; +import com.openhis.administration.domain.Patient; +import com.openhis.administration.domain.Practitioner; +import com.openhis.administration.mapper.EncounterDiagnosisMapper; +import com.openhis.administration.mapper.EncounterMapper; +import com.openhis.administration.mapper.OrganizationMapper; +import com.openhis.administration.mapper.PatientMapper; +import com.openhis.administration.mapper.PractitionerMapper; +import com.openhis.clinical.domain.Condition; +import com.openhis.clinical.mapper.ConditionMapper; +import com.openhis.workflow.domain.ServiceRequest; +import com.openhis.workflow.service.IServiceRequestService; +import com.openhis.common.enums.RequestStatus; +import com.openhis.common.enums.GenerateSource; +import com.openhis.common.enums.ItemType; +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.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.web.consultation.domain.ConsultationRequest; +import com.openhis.web.consultation.domain.ConsultationInvited; +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; +import com.openhis.administration.domain.ChargeItemDefinition; +import com.openhis.administration.mapper.ChargeItemDefinitionMapper; +import com.openhis.administration.domain.ChargeItem; +import com.openhis.administration.service.IChargeItemService; +import com.openhis.common.enums.ChargeItemStatus; +import com.openhis.web.doctorstation.mapper.DoctorStationAdviceAppMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +/** + * 会诊管理AppService实现类 + * + * @author system + * @date 2026-01-29 + */ +@Slf4j +@Service +public class ConsultationAppServiceImpl implements IConsultationAppService { + + @Resource + private ConsultationRequestMapper consultationRequestMapper; + + @Resource + private ConsultationInvitedMapper consultationInvitedMapper; + + @Resource + private EncounterMapper encounterMapper; + + @Resource + private PatientMapper patientMapper; + + @Resource + private ConditionMapper conditionMapper; + + @Resource + private OrganizationMapper organizationMapper; + + @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; + + /** + * 用于生成唯一序列号的计数器 + * 每秒重置,范围 0000-9999 + */ + private static final AtomicInteger SEQUENCE = new AtomicInteger(0); + + /** + * 记录上一次生成ID的时间(秒级) + */ + private static volatile long lastSecond = 0L; + + @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 + @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()); + } + } else { + // 新增:创建新记录 + entity = new ConsultationRequest(); + entity.setConsultationId(generateConsultationId()); + entity.setTenantId(SecurityUtils.getLoginUser().getTenantId().longValue()); + entity.setConsultationRequestDate(new Date()); + } + + // 复制基本属性(现在字段名已统一,可以直接复制) + BeanUtils.copyProperties(dto, entity, "id", "consultationId", "invitedList", "submitFlag", "provisionalDiagnosis", "consultationRequestDate"); + + // 如果前端没有传递申请医生ID,使用当前登录用户 + 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(",")); + entity.setInvitedObject(invitedObjectText); + } + + // 处理门诊诊断:如果前端没有传递,自动从数据库获取 + if (!StringUtils.hasText(dto.getProvisionalDiagnosis()) && dto.getEncounterId() != null) { + String diagnosis = getPatientDiagnosis(dto.getEncounterId()); + if (StringUtils.hasText(diagnosis)) { + entity.setProvisionalDiagnosis(diagnosis); + } + } else { + entity.setProvisionalDiagnosis(dto.getProvisionalDiagnosis()); + } + + // 设置会诊状态(使用枚举) + if (Boolean.TRUE.equals(dto.getSubmitFlag())) { + entity.setConsultationStatus(ConsultationStatusEnum.SUBMITTED.getCode()); + entity.setConfirmingPhysician(SecurityUtils.getLoginUser().getUser().getNickName()); + entity.setConfirmingPhysicianId(SecurityUtils.getLoginUser().getPractitionerId()); + entity.setConfirmingDate(new Date()); + } else { + entity.setConsultationStatus(ConsultationStatusEnum.NEW.getCode()); + } + + // 保存或更新主表 + 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()) { + ConsultationInvited invited = new ConsultationInvited(); + invited.setConsultationRequestId(entity.getId()); + invited.setInvitedDepartmentId(invitedDto.getDeptId()); + invited.setInvitedDepartmentName(invitedDto.getDeptName()); + invited.setInvitedPhysicianId(invitedDto.getPhysicianId()); + invited.setInvitedPhysicianName(invitedDto.getPhysicianName()); + invited.setInvitedStatus(InvitedStatusEnum.PENDING.getCode()); + invited.setTenantId(entity.getTenantId()); + + consultationInvitedMapper.insert(invited); + } + } + + return true; + } catch (Exception e) { + log.error("保存会诊申请失败", e); + throw new RuntimeException("保存会诊申请失败: " + e.getMessage()); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean submitConsultation(String consultationId) { + try { + if (!StringUtils.hasText(consultationId)) { + throw new IllegalArgumentException("会诊申请单号不能为空"); + } + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(ConsultationRequest::getConsultationId, consultationId); + + ConsultationRequest entity = consultationRequestMapper.selectOne(wrapper); + if (entity == null) { + throw new IllegalArgumentException("会诊申请不存在"); + } + + // 只有新开状态才能提交 + if (!ConsultationStatusEnum.NEW.getCode().equals(entity.getConsultationStatus())) { + throw new IllegalArgumentException("只有新开状态的会诊申请才能提交"); + } + + // 更新状态为已提交 + entity.setConsultationStatus(ConsultationStatusEnum.SUBMITTED.getCode()); + + // 设置提交会诊的医生信息 + entity.setConfirmingPhysician(SecurityUtils.getLoginUser().getUser().getNickName()); + entity.setConfirmingPhysicianId(SecurityUtils.getLoginUser().getPractitionerId()); + entity.setConfirmingDate(new Date()); + + consultationRequestMapper.updateById(entity); + + // 新增:更新门诊医嘱表状态为已提交 + updateServiceRequestStatus(entity.getOrderId(), RequestStatus.ACTIVE.getValue()); + return true; + } catch (Exception e) { + log.error("提交会诊申请失败", e); + throw new RuntimeException("提交会诊申请失败: " + e.getMessage()); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean cancelConsultation(String consultationId, String cancelReason) { + try { + if (!StringUtils.hasText(consultationId)) { + throw new IllegalArgumentException("会诊申请单号不能为空"); + } + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(ConsultationRequest::getConsultationId, consultationId); + + ConsultationRequest entity = consultationRequestMapper.selectOne(wrapper); + if (entity == null) { + throw new IllegalArgumentException("会诊申请不存在"); + } + + // 判断是"取消提交"还是"作废" + if ("取消提交".equals(cancelReason) && ConsultationStatusEnum.SUBMITTED.getCode().equals(entity.getConsultationStatus())) { + // 取消提交:将状态从"已提交"改回"新开" + entity.setConsultationStatus(ConsultationStatusEnum.NEW.getCode()); + entity.setConfirmingPhysician(null); + 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(new Date()); + consultationRequestMapper.updateById(entity); + + // 更新门诊医嘱表状态为已作废 + updateServiceRequestStatus(entity.getOrderId(), RequestStatus.CANCELLED.getValue()); + + } + + return true; + } catch (Exception e) { + log.error("操作失败", e); + throw new RuntimeException("操作失败: " + e.getMessage()); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean completeConsultation(String consultationId) { + try { + if (!StringUtils.hasText(consultationId)) { + throw new IllegalArgumentException("会诊申请单号不能为空"); + } + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(ConsultationRequest::getConsultationId, consultationId); + + ConsultationRequest entity = consultationRequestMapper.selectOne(wrapper); + if (entity == null) { + throw new IllegalArgumentException("会诊申请不存在"); + } + + // 更新状态为已完成 + entity.setConsultationStatus(ConsultationStatusEnum.COMPLETED.getCode()); + consultationRequestMapper.updateById(entity); + + // 🎯 新增:更新门诊医嘱表状态为已完成 + updateServiceRequestStatus(entity.getOrderId(), RequestStatus.COMPLETED.getValue()); + + + return true; + } catch (Exception e) { + log.error("结束会诊申请失败", e); + throw new RuntimeException("结束会诊申请失败: " + e.getMessage()); + } + } + + @Override + public List getDepartmentTree() { + try { + // 查询所有科室 + LambdaQueryWrapper deptWrapper = new LambdaQueryWrapper<>(); + 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() + .filter(p -> p.getOrgId() != null) + .collect(Collectors.groupingBy(Practitioner::getOrgId)); + + // 构建树形结构 + List treeList = new ArrayList<>(); + for (Organization dept : deptList) { + DepartmentTreeDto treeDto = new DepartmentTreeDto(); + treeDto.setId(dept.getId()); + treeDto.setLabel(dept.getName()); + + // 添加该科室的医生 + List physicians = practitionerMap.get(dept.getId()); + if (physicians != null && !physicians.isEmpty()) { + List children = physicians.stream() + .map(p -> { + PhysicianNodeDto node = new PhysicianNodeDto(); + node.setId(p.getId()); + node.setLabel(p.getName()); + return node; + }) + .collect(Collectors.toList()); + treeDto.setChildren(children); + } else { + treeDto.setChildren(new ArrayList<>()); + } + + treeList.add(treeDto); + } + + + return treeList; + } catch (Exception e) { + log.error("获取科室医生树失败", e); + throw new RuntimeException("获取科室医生树失败: " + e.getMessage()); + } + } + + @Override + public Map getMainDiagnosis(Long encounterId) { + Map result = new HashMap<>(); + + if (encounterId == null) { + return result; + } + + try { + String diagnosis = getPatientDiagnosis(encounterId); + if (StringUtils.hasText(diagnosis)) { + result.put("diagnosis", diagnosis); + } + return result; + } catch (Exception e) { + log.error("获取主诊断失败", e); + return result; + } + } + + /** + * 获取患者诊断信息 + * + * @param encounterId 就诊ID + * @return 诊断信息字符串 + */ + private String getPatientDiagnosis(Long encounterId) { + try { + // 查询该就诊的所有诊断,优先查询主诊断 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(EncounterDiagnosis::getEncounterId, encounterId) + .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)) { + 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 { + // 获取当前登录医生ID + Long currentPhysicianId = SecurityUtils.getLoginUser().getPractitionerId(); + + // 查询邀请我的会诊申请 + 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); + // 添加我的邀请状态 + ConsultationInvited myInvited = invitedMap.get(request.getId()); + if (myInvited != null) { + dto.setMyInvitedStatus(myInvited.getInvitedStatus()); + dto.setMyConfirmTime(myInvited.getConfirmTime()); + } + return dto; + }).collect(Collectors.toList()); + + } catch (Exception e) { + log.error("获取我的会诊邀请失败", e); + throw new RuntimeException("获取我的会诊邀请失败: " + e.getMessage()); + } + } + + /** + * 转换为DTO + */ + private ConsultationRequestDto convertToDto(ConsultationRequest entity) { + ConsultationRequestDto dto = new ConsultationRequestDto(); + BeanUtils.copyProperties(entity, dto); + + // 查询邀请对象列表 + if (entity.getId() != null) { + 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(); + invitedDto.setDeptId(invited.getInvitedDepartmentId()); + invitedDto.setDeptName(invited.getInvitedDepartmentName()); + invitedDto.setPhysicianId(invited.getInvitedPhysicianId()); + invitedDto.setPhysicianName(invited.getInvitedPhysicianName()); + return invitedDto; + }).collect(Collectors.toList()); + + dto.setInvitedList(invitedDtoList); + } + } + + // 如果门诊诊断为空,尝试从数据库自动获取 + if (!StringUtils.hasText(entity.getProvisionalDiagnosis()) && entity.getEncounterId() != null) { + String diagnosis = getPatientDiagnosis(entity.getEncounterId()); + if (StringUtils.hasText(diagnosis)) { + dto.setProvisionalDiagnosis(diagnosis); + } + } + + return dto; + } + + /** + * 生成会诊申请单号(高性能版本) + * 格式:CS + 年月日时分秒 + 4位序列号 + * 例如:CS202601301425300001 + * + * 优化说明: + * 1. 使用 AtomicInteger 保证线程安全,性能远高于 synchronized + * 2. 每秒最多支持 10000 个并发请求(0000-9999) + * 3. 无需查询数据库,性能提升 100+ 倍 + * 4. 秒级时间戳 + 自增序列号,确保唯一性 + */ + private String generateConsultationId() { + + while (true) { + long nowMillis = System.currentTimeMillis(); + long currentSecond = nowMillis / 1000; + + // 跨秒则重置序列号(双重检查 + 类锁) + if (currentSecond != lastSecond) { + synchronized (ConsultationAppServiceImpl.class) { + if (currentSecond != lastSecond) { + lastSecond = currentSecond; + SEQUENCE.set(0); + } + } + } + + int sequence = SEQUENCE.getAndIncrement(); + + // 本秒内序列号耗尽:精确等待到下一秒再重试(避免递归、避免固定100ms) + if (sequence >= 10000) { + // 可选“回退自增”让 SEQUENCE 不乱飙 + // 回退这次自增(可选,但更“稳”,避免 SEQUENCE 无限增长) + // 注意:回退可能在极端并发下导致轻微争用,但比不回退更可控 + SEQUENCE.decrementAndGet(); + + sleepUntilNextSecond(nowMillis); + continue; + } + 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; + } + + } + + // 精确等到下一秒,等待更短、抖动更小,不在固定睡100ms + private void sleepUntilNextSecond(long nowMillis) { + long nextSecondMillis = (nowMillis / 1000 + 1) * 1000; + long sleepMillis = nextSecondMillis - nowMillis; + + // 防御:理论上 sleepMillis 范围是 1~1000ms + if (sleepMillis <= 0) return; + + try { + Thread.sleep(sleepMillis); + } catch (InterruptedException e) { + // 恢复中断标记:把“有人希望我停下来”的信号传递给上层 + Thread.currentThread().interrupt(); + throw new RuntimeException("生成会诊申请单号失败:线程被中断", e); + } + } + +// private String generateConsultationId() { +// java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyyMMddHHmmss"); +// long currentSecond = System.currentTimeMillis() / 1000; +// +// // 如果是新的一秒,重置序列号 +// // 跨秒检测:如果发现当前秒变了,说明进入下一秒 +// // 重置序列号:把 SEQUENCE 重新设置为 0,保证新的一秒从 0000 开始 +// // 双重锁检测:避免重复进入 +// if (currentSecond != lastSecond) { +// synchronized (ConsultationAppServiceImpl.class) { +// if (currentSecond != lastSecond) { +// lastSecond = currentSecond; +// SEQUENCE.set(0); +// } +// } +// } +// +// // 获取当前序列号并自增(线程安全) +// int sequence = SEQUENCE.getAndIncrement(); +// +// // 如果同一秒内超过 10000 个请求,等待下一秒 +// if (sequence >= 10000) { +// try { +// Thread.sleep(100); // 等待 100ms +// return generateConsultationId(); // 递归重试 +// } catch (InterruptedException e) { +// Thread.currentThread().interrupt(); +// throw new RuntimeException("生成会诊申请单号失败", e); +// } +// } +// +// // 生成时间戳部分 +// String dateTime = sdf.format(new Date()); +// +// // 生成4位序列号(补零) +// String sequenceStr = String.format("%04d", sequence); +// +// return "CS" + dateTime + sequenceStr; +// } + + /** + * 计算年龄 + */ + 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); + + 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) { + // 如果前端没有传递,使用默认的"院内会诊" + 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()); + + // 🎯 设置执行次数为 1 + serviceRequest.setQuantity(BigDecimal.ONE); + serviceRequest.setUnitCode("111"); // 单位:次 + + // 医嘱状态:新开 + serviceRequest.setStatusEnum(RequestStatus.DRAFT.getValue()); + + // 医嘱内容:保存会诊申请的详细信息(JSON格式) + Map contentMap = new HashMap<>(); + contentMap.put("consultationRequestId", consultationRequest.getId()); + contentMap.put("consultationId", consultationRequest.getConsultationId()); + contentMap.put("consultationType", consultationRequest.getConsultationActivityName()); + contentMap.put("consultationActivityId", activityDefinitionId); + contentMap.put("patientName", consultationRequest.getPatientName()); + contentMap.put("provisionalDiagnosis", consultationRequest.getProvisionalDiagnosis()); + contentMap.put("consultationPurpose", consultationRequest.getConsultationPurpose()); + contentMap.put("requestingPhysician", consultationRequest.getRequestingPhysician()); + contentMap.put("department", consultationRequest.getDepartment()); + contentMap.put("adviceName", consultationRequest.getConsultationActivityName()); // 添加项目名称 + + serviceRequest.setContentJson(JSON.toJSONString(contentMap)); + + // 时间信息 + serviceRequest.setAuthoredTime(new Date()); + + // 审计字段 + serviceRequest.setTenantId(SecurityUtils.getLoginUser().getTenantId().intValue()); + serviceRequest.setCreateBy(SecurityUtils.getUsername()); + serviceRequest.setCreateTime(new Date()); + + // 保存到数据库 + iServiceRequestService.save(serviceRequest); + + + // 🎯 新增:创建费用项(ChargeItem) + // 查询会诊项目的价格定义 + LambdaQueryWrapper priceWrapper = new LambdaQueryWrapper<>(); + priceWrapper.eq(ChargeItemDefinition::getInstanceTable, "wor_activity_definition") + .eq(ChargeItemDefinition::getInstanceId, activityDefinitionId) + .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); + + 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)); + chargeItem.setGenerateSourceEnum(GenerateSource.DOCTOR_PRESCRIPTION.getValue()); + chargeItem.setPatientId(consultationRequest.getPatientId()); + chargeItem.setContextEnum(ItemType.ACTIVITY.getValue()); // 诊疗类型 + chargeItem.setEncounterId(consultationRequest.getEncounterId()); + + // 🎯 设置费用性质(必填字段) + chargeItem.setAccountId(accountId); + + // 费用定价信息 + chargeItem.setDefinitionId(chargeItemDef.getId()); + chargeItem.setUnitPrice(chargeItemDef.getPrice()); + 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 Date()); + + // 审计字段 + chargeItem.setTenantId(SecurityUtils.getLoginUser().getTenantId()); + chargeItem.setCreateBy(SecurityUtils.getUsername()); + chargeItem.setCreateTime(new Date()); + + iChargeItemService.save(chargeItem); + + } else { + log.warn("未找到会诊项目的价格定义,activityId: {}", activityDefinitionId); + } + + // 将医嘱ID保存到会诊申请表 + consultationRequest.setOrderId(serviceRequest.getId()); + consultationRequestMapper.updateById(consultationRequest); + } catch (Exception e) { + log.error("保存会诊医嘱失败", e); + throw new RuntimeException("保存会诊医嘱失败: " + e.getMessage()); + } + } + + /** + * 更新会诊医嘱 + * + * @param consultationRequest 会诊申请实体 + */ + private void updateConsultationServiceRequest(ConsultationRequest consultationRequest) { + try { + if (consultationRequest.getOrderId() == null) { + 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 Date()); + + iServiceRequestService.updateById(serviceRequest); + + } catch (Exception e) { + log.error("更新会诊医嘱失败", e); + throw new RuntimeException("更新会诊医嘱失败: " + e.getMessage()); + } + } + + /** + * 更新门诊医嘱状态 + * + * @param orderId 医嘱ID + * @param status 新状态 + */ + private void updateServiceRequestStatus(Long orderId, Integer status) { + try { + if (orderId == null) { + log.warn("医嘱ID为空,跳过状态更新"); + return; + } + + ServiceRequest serviceRequest = iServiceRequestService.getById(orderId); + if (serviceRequest == null) { + log.warn("未找到医嘱记录,OrderId: {}", orderId); + return; + } + + serviceRequest.setStatusEnum(status); + serviceRequest.setUpdateBy(SecurityUtils.getUsername()); + serviceRequest.setUpdateTime(new Date()); + + iServiceRequestService.updateById(serviceRequest); + + } catch (Exception e) { + log.error("更新医嘱状态失败", e); + throw new RuntimeException("更新医嘱状态失败: " + e.getMessage()); + } + } + + @Override + public List getConsultationActivities() { + try { + // 查询所有会诊相关的诊疗活动 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.like(ActivityDefinition::getName, "会诊") + .eq(ActivityDefinition::getDeleteFlag, "0") + .orderBy(true, true, ActivityDefinition::getSortOrder); + + List activityList = activityDefinitionMapper.selectList(wrapper); + if (activityList.isEmpty()) { + return new ArrayList<>(); + } + + // 转换为DTO并查询价格 + List resultList = new ArrayList<>(); + for (ActivityDefinition activity : activityList) { + ConsultationActivityDto dto = new ConsultationActivityDto(); + dto.setId(activity.getId()); + dto.setName(activity.getName()); + dto.setBusNo(activity.getBusNo()); + dto.setTypeEnum(activity.getTypeEnum()); + dto.setCategoryCode(activity.getCategoryCode()); + dto.setUnitCode(activity.getPermittedUnitCode()); + dto.setStatusEnum(activity.getStatusEnum()); + + // 查询价格 + LambdaQueryWrapper priceWrapper = new LambdaQueryWrapper<>(); + priceWrapper.eq(ChargeItemDefinition::getInstanceTable, "wor_activity_definition") + .eq(ChargeItemDefinition::getInstanceId, activity.getId()) + .eq(ChargeItemDefinition::getStatusEnum, 2) // 2=启用 + .eq(ChargeItemDefinition::getDeleteFlag, "0") + .last("LIMIT 1"); + + ChargeItemDefinition chargeItem = chargeItemDefinitionMapper.selectOne(priceWrapper); + if (chargeItem != null && chargeItem.getPrice() != null) { + dto.setPrice(chargeItem.getPrice()); + } else { + dto.setPrice(java.math.BigDecimal.ZERO); + log.warn("会诊项目: {} 未找到价格定义", activity.getName()); + } + + resultList.add(dto); + } + return resultList; + } catch (Exception e) { + log.error("查询会诊项目列表失败", e); + throw new RuntimeException("查询会诊项目列表失败: " + e.getMessage()); + } + } +} + 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 new file mode 100644 index 00000000..1e7b2723 --- /dev/null +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/controller/ConsultationController.java @@ -0,0 +1,175 @@ +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.ConsultationRequestDto; +import com.openhis.web.consultation.dto.DepartmentTreeDto; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.List; +import java.util.Map; + +/** + * 会诊管理Controller + * + * @author system + * @date 2026-01-29 + */ +@Slf4j +@Api(tags = "会诊管理") +@RestController +@RequestMapping("/consultation") +public class ConsultationController { + + @Resource + private IConsultationAppService consultationAppService; + + /** + * 获取会诊列表 + */ + @ApiOperation("获取会诊列表") + @GetMapping("/list") + public R> getConsultationList( + @ApiParam("就诊ID(可选,不传则查询当前医生的所有会诊申请)") + @RequestParam(required = false) Long encounterId) { + try { + List list = consultationAppService.getConsultationList(encounterId); + return R.ok(list); + } catch (Exception e) { + log.error("获取会诊列表失败", e); + return R.fail("获取会诊列表失败: " + e.getMessage()); + } + } + + /** + * 保存会诊申请 + */ + @ApiOperation("保存会诊申请") + @PostMapping("/save") + public R saveConsultation(@RequestBody ConsultationRequestDto dto) { + try { + Boolean result = consultationAppService.saveConsultation(dto); + return result ? R.ok("保存成功") : R.fail("保存失败"); + } catch (Exception e) { + log.error("保存会诊申请失败", e); + return R.fail("保存会诊申请失败: " + e.getMessage()); + } + } + + /** + * 提交会诊申请 + */ + @ApiOperation("提交会诊申请") + @PostMapping("/submit") + public R submitConsultation( + @ApiParam("会诊申请单号") @RequestParam String consultationId) { + try { + Boolean result = consultationAppService.submitConsultation(consultationId); + return result ? R.ok("提交成功") : R.fail("提交失败"); + } catch (Exception e) { + log.error("提交会诊申请失败", e); + return R.fail("提交会诊申请失败: " + e.getMessage()); + } + } + + /** + * 作废会诊申请 + */ + @ApiOperation("作废会诊申请") + @PostMapping("/cancel") + public R cancelConsultation( + @ApiParam("会诊申请单号") @RequestParam String consultationId, + @ApiParam("作废原因") @RequestParam(required = false) String cancelReason) { + try { + Boolean result = consultationAppService.cancelConsultation(consultationId, cancelReason); + return result ? R.ok("作废成功") : R.fail("作废失败"); + } catch (Exception e) { + log.error("作废会诊申请失败", e); + return R.fail("作废会诊申请失败: " + e.getMessage()); + } + } + + /** + * 结束会诊申请 + */ + @ApiOperation("结束会诊申请") + @PostMapping("/complete") + public R completeConsultation( + @ApiParam("会诊申请单号") @RequestParam String consultationId) { + try { + Boolean result = consultationAppService.completeConsultation(consultationId); + return result ? R.ok("结束成功") : R.fail("结束失败"); + } catch (Exception e) { + log.error("结束会诊申请失败", e); + return R.fail("结束会诊申请失败: " + e.getMessage()); + } + } + + /** + * 获取科室医生树 + */ + @ApiOperation("获取科室医生树") + @GetMapping("/departmentTree") + public R> getDepartmentTree() { + try { + List tree = consultationAppService.getDepartmentTree(); + return R.ok(tree); + } catch (Exception e) { + log.error("获取科室医生树失败", e); + return R.fail("获取科室医生树失败: " + e.getMessage()); + } + } + + /** + * 获取主诊断 + */ + @ApiOperation("获取主诊断") + @GetMapping("/mainDiagnosis") + public R> getMainDiagnosis( + @ApiParam("就诊ID") @RequestParam Long encounterId) { + try { + Map diagnosis = consultationAppService.getMainDiagnosis(encounterId); + return R.ok(diagnosis); + } catch (Exception e) { + log.error("获取主诊断失败", e); + return R.fail("获取主诊断失败: " + e.getMessage()); + } + } + + /** + * 获取我的会诊邀请列表 + */ + @ApiOperation("获取我的会诊邀请列表") + @GetMapping("/myInvitations") + public R> getMyInvitations() { + try { + List list = consultationAppService.getMyInvitations(); + return R.ok(list); + } catch (Exception e) { + log.error("获取我的会诊邀请失败", e); + return R.fail("获取我的会诊邀请失败: " + e.getMessage()); + } + } + + /** + * 获取所有会诊项目及价格 + */ + @ApiOperation("获取所有会诊项目及价格") + @GetMapping("/activities") + public R> getConsultationActivities() { + try { + List list = consultationAppService.getConsultationActivities(); + return R.ok(list); + } 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/ConsultationInvited.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/domain/ConsultationInvited.java new file mode 100644 index 00000000..d3739461 --- /dev/null +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/domain/ConsultationInvited.java @@ -0,0 +1,116 @@ +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-01-30 + */ +@Data +@TableName("consultation_invited") +public class ConsultationInvited implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private Long id; + + /** + * 会诊申请ID(外键) + */ + @TableField("consultation_request_id") + private Long consultationRequestId; + + /** + * 邀请科室ID + */ + @TableField("invited_department_id") + private Long invitedDepartmentId; + + /** + * 邀请科室名称 + */ + @TableField("invited_department_name") + private String invitedDepartmentName; + + /** + * 邀请医生ID + */ + @TableField("invited_physician_id") + private Long invitedPhysicianId; + + /** + * 邀请医生姓名 + */ + @TableField("invited_physician_name") + private String invitedPhysicianName; + + /** + * 邀请状态(0-待确认,1-已确认,2-已拒绝) + */ + @TableField("invited_status") + private Integer invitedStatus; + + /** + * 确认时间 + */ + @TableField("confirm_time") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date confirmTime; + + /** + * 确认意见 + */ + @TableField("confirm_opinion") + private String confirmOpinion; + + /** + * 创建时间 + */ + @TableField(value = "create_time", fill = FieldFill.INSERT) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** + * 更新时间 + */ + @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; +} + 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 new file mode 100644 index 00000000..c6adb80b --- /dev/null +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/domain/ConsultationRequest.java @@ -0,0 +1,264 @@ +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-01-29 + */ +@Data +@TableName("consultation_request") +public class ConsultationRequest implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private Long id; + + /** + * 会诊申请单号 + */ + @TableField("consultation_id") + private String consultationId; + + /** + * 患者ID + */ + @TableField("patient_id") + private Long patientId; + + /** + * 就诊ID + */ + @TableField("encounter_id") + private Long encounterId; + + /** + * 订单ID(关联门诊医嘱表 wor_service_request.id) + */ + @TableField("order_id") + private Long orderId; + + /** + * 患者姓名 + */ + @TableField("patient_name") + private String patientName; + + /** + * 患者病历号 + */ + @TableField("patient_bus_no") + private String patientBusNo; + + /** + * 患者就诊卡号 + */ + @TableField("patient_identifier_no") + private String patientIdentifierNo; + + /** + * 性别 + */ + @TableField("gender_enum") + private Integer genderEnum; + + /** + * 年龄 + */ + @TableField("age") + private Integer age; + + /** + * 申请科室名称 + */ + @TableField("department") + private String department; + + /** + * 申请科室ID + */ + @TableField("department_id") + private Long departmentId; + + /** + * 申请医生姓名 + */ + @TableField("requesting_physician") + private String requestingPhysician; + + /** + * 申请医生ID + */ + @TableField("requesting_physician_id") + private Long requestingPhysicianId; + + /** + * 申请时间 + */ + @TableField("consultation_request_date") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date consultationRequestDate; + + /** + * 邀请对象显示文本 + */ + @TableField("invited_object") + private String invitedObject; + + /** + * 会诊时间 + */ + @TableField("consultation_date") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date consultationDate; + + /** + * 会诊目的 + */ + @TableField("consultation_purpose") + private String consultationPurpose; + + /** + * 门诊诊断 + */ + @TableField("provisional_diagnosis") + private String provisionalDiagnosis; + + /** + * 会诊项目ID(关联wor_activity_definition表) + */ + @TableField("consultation_activity_id") + private Long consultationActivityId; + + /** + * 会诊项目名称(如:院内会诊、远程会诊等) + */ + @TableField("consultation_activity_name") + private String consultationActivityName; + + /** + * 会诊意见 + */ + @TableField("consultation_opinion") + private String consultationOpinion; + + /** + * 会诊状态(0-新开,10-已提交,20-已确认,30-已签名,40-已完成,50-已取消) + */ + @TableField("consultation_status") + private Integer consultationStatus; + + /** + * 紧急程度(一般/紧急) + */ + @TableField("consultation_urgency") + private String consultationUrgency; + + /** + * 确认医生 + */ + @TableField("confirming_physician") + private String confirmingPhysician; + + /** + * 确认医生ID + */ + @TableField("confirming_physician_id") + private Long confirmingPhysicianId; + + /** + * 确认时间 + */ + @TableField("confirming_date") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date confirmingDate; + + /** + * 签名 + */ + @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("cancel_nature_date") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date cancelNatureDate; + + /** + * 作废原因 + */ + @TableField("cancel_reason") + private String cancelReason; + + /** + * 创建时间 + */ + @TableField(value = "create_time", fill = FieldFill.INSERT) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** + * 更新时间 + */ + @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/dto/ConsultationActivityDto.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/dto/ConsultationActivityDto.java new file mode 100644 index 00000000..ab8e3870 --- /dev/null +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/dto/ConsultationActivityDto.java @@ -0,0 +1,68 @@ +package com.openhis.web.consultation.dto; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * 会诊项目DTO + * + * @author system + * @date 2026-02-02 + */ +@Data +public class ConsultationActivityDto implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 项目ID + * 使用 @JsonSerialize 注解将 Long 类型转换为 String,避免前端 JavaScript 精度丢失 + */ + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + + /** + * 项目名称 + */ + private String name; + + /** + * 项目编号 + */ + private String busNo; + + /** + * 价格 + */ + private BigDecimal price; + + /** + * 类型枚举 + */ + private Integer typeEnum; + + /** + * 分类编码 + */ + private String categoryCode; + + /** + * 单位编码 + */ + private String unitCode; + + /** + * 单位名称 + */ + private String unitCodeText; + + /** + * 状态 + */ + private Integer statusEnum; +} + 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 new file mode 100644 index 00000000..faab88cf --- /dev/null +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/dto/ConsultationRequestDto.java @@ -0,0 +1,190 @@ +package com.openhis.web.consultation.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** + * 会诊申请DTO + * + * @author system + * @date 2026-01-29 + */ +@Data +public class ConsultationRequestDto implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + + /** + * 会诊申请单号 + */ + private String consultationId; + + /** + * 患者ID + */ + @JsonSerialize(using = ToStringSerializer.class) + private Long patientId; + + /** + * 患者姓名 + */ + private String patientName; + + /** + * 患者性别 + */ + private Integer genderEnum; + + /** + * 患者年龄 + */ + private Integer age; + + /** + * 病历号 + */ + private String patientBusNo; + + /** + * 就诊卡号 + */ + private String patientIdentifierNo; + + /** + * 就诊ID + */ + @JsonSerialize(using = ToStringSerializer.class) + private Long encounterId; + + /** + * 申请科室ID + */ + @JsonSerialize(using = ToStringSerializer.class) + private Long departmentId; + + /** + * 申请科室名称 + */ + private String department; + + /** + * 申请医生ID + */ + @JsonSerialize(using = ToStringSerializer.class) + private Long requestingPhysicianId; + + /** + * 申请医生姓名 + */ + private String requestingPhysician; + + /** + * 邀请对象显示文本 + */ + private String invitedObject; + + /** + * 邀请对象列表 + */ + private List invitedList; + + /** + * 会诊时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date consultationDate; + + /** + * 申请时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date consultationRequestDate; + + /** + * 会诊目的 + */ + private String consultationPurpose; + + /** + * 门诊诊断 + */ + private String provisionalDiagnosis; + + /** + * 会诊项目ID(关联wor_activity_definition表) + */ + @JsonSerialize(using = ToStringSerializer.class) + private Long consultationActivityId; + + /** + * 会诊项目名称(如:院内会诊、远程会诊等) + */ + private String consultationActivityName; + + /** + * 紧急程度(1=普通,2=紧急) + */ + private String consultationUrgency; + + /** + * 会诊状态 + */ + private Integer consultationStatus; + + /** + * 会诊意见 + */ + private String consultationOpinion; + + /** + * 参与会诊医师 + */ + private String consultingPhysicians; + + /** + * 签名医生ID + */ + @JsonSerialize(using = ToStringSerializer.class) + private Long signingPhysicianId; + + /** + * 签名医生姓名 + */ + private String signingPhysicianName; + + /** + * 签名时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date signingTime; + + /** + * 是否提交(保存时使用) + */ + private Boolean submitFlag; + + /** + * 我的邀请状态(查询我的邀请时使用,0-待确认,1-已确认,2-已拒绝) + */ + private Integer myInvitedStatus; + + /** + * 我的确认时间(查询我的邀请时使用) + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date myConfirmTime; +} + + diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/dto/DepartmentTreeDto.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/dto/DepartmentTreeDto.java new file mode 100644 index 00000000..18abc3ab --- /dev/null +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/dto/DepartmentTreeDto.java @@ -0,0 +1,38 @@ +package com.openhis.web.consultation.dto; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 科室医生树DTO + * + * @author system + * @date 2026-01-29 + */ +@Data +public class DepartmentTreeDto implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 科室ID(序列化为字符串,避免JavaScript精度丢失) + */ + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + + /** + * 科室名称 + */ + private String label; + + /** + * 子节点(医生列表) + */ + private List children; +} + + 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 new file mode 100644 index 00000000..29fd3c6a --- /dev/null +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/dto/InvitedObjectDto.java @@ -0,0 +1,43 @@ +package com.openhis.web.consultation.dto; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import lombok.Data; + +import java.io.Serializable; + +/** + * 邀请对象DTO + * + * @author system + * @date 2026-01-29 + */ +@Data +public class InvitedObjectDto implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 科室ID(序列化为字符串,避免JavaScript精度丢失) + */ + @JsonSerialize(using = ToStringSerializer.class) + private Long deptId; + + /** + * 科室名称 + */ + private String deptName; + + /** + * 医生ID(为空表示邀请整个科室)(序列化为字符串,避免JavaScript精度丢失) + */ + @JsonSerialize(using = ToStringSerializer.class) + private Long physicianId; + + /** + * 医生姓名 + */ + private String physicianName; +} + + diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/dto/PhysicianNodeDto.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/dto/PhysicianNodeDto.java new file mode 100644 index 00000000..54cfb9d8 --- /dev/null +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/dto/PhysicianNodeDto.java @@ -0,0 +1,32 @@ +package com.openhis.web.consultation.dto; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import lombok.Data; + +import java.io.Serializable; + +/** + * 医生节点DTO + * + * @author system + * @date 2026-01-29 + */ +@Data +public class PhysicianNodeDto implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 医生ID(序列化为字符串,避免JavaScript精度丢失) + */ + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + + /** + * 医生姓名 + */ + private String label; +} + + 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 new file mode 100644 index 00000000..981b7398 --- /dev/null +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/enums/ConsultationStatusEnum.java @@ -0,0 +1,85 @@ +package com.openhis.web.consultation.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 会诊状态枚举 + * + * @author system + * @date 2026-02-05 + */ +@Getter +@AllArgsConstructor +public enum ConsultationStatusEnum { + + /** + * 新开(草稿) + */ + NEW(0, "新开"), + + /** + * 已提交 + */ + SUBMITTED(10, "已提交"), + + /** + * 已确认 + */ + CONFIRMED(20, "已确认"), + + /** + * 已签名 + */ + SIGNED(30, "已签名"), + + /** + * 已完成 + */ + COMPLETED(40, "已完成"), + + /** + * 已取消 + */ + CANCELLED(50, "已取消"); + + /** + * 状态码 + */ + private final Integer code; + + /** + * 状态描述 + */ + private final String description; + + /** + * 根据状态码获取枚举 + */ + public static ConsultationStatusEnum getByCode(Integer code) { + if (code == null) { + return null; + } + for (ConsultationStatusEnum status : values()) { + if (status.getCode().equals(code)) { + return status; + } + } + return null; + } + + /** + * 判断是否可以编辑 + */ + public boolean canEdit() { + return this == NEW || this == SUBMITTED; + } + + /** + * 判断是否可以取消 + */ + public boolean canCancel() { + return this == NEW || this == SUBMITTED || this == CONFIRMED; + } +} + 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 new file mode 100644 index 00000000..65df3c9b --- /dev/null +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/enums/ConsultationUrgencyEnum.java @@ -0,0 +1,51 @@ +package com.openhis.web.consultation.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 会诊紧急程度枚举 + * + * @author system + * @date 2026-02-05 + */ +@Getter +@AllArgsConstructor +public enum ConsultationUrgencyEnum { + + /** + * 普通 + */ + NORMAL("1", "普通"), + + /** + * 紧急 + */ + URGENT("2", "紧急"); + + /** + * 类型码 + */ + private final String code; + + /** + * 类型描述 + */ + private final String description; + + /** + * 根据类型码获取枚举 + */ + public static ConsultationUrgencyEnum getByCode(String code) { + if (code == null) { + return null; + } + for (ConsultationUrgencyEnum urgency : values()) { + if (urgency.getCode().equals(code)) { + return urgency; + } + } + return null; + } +} + 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 new file mode 100644 index 00000000..eeada950 --- /dev/null +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/enums/InvitedStatusEnum.java @@ -0,0 +1,56 @@ +package com.openhis.web.consultation.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 邀请状态枚举 + * + * @author system + * @date 2026-02-05 + */ +@Getter +@AllArgsConstructor +public enum InvitedStatusEnum { + + /** + * 待确认 + */ + PENDING(0, "待确认"), + + /** + * 已确认 + */ + CONFIRMED(1, "已确认"), + + /** + * 已拒绝 + */ + REJECTED(2, "已拒绝"); + + /** + * 状态码 + */ + private final Integer code; + + /** + * 状态描述 + */ + private final String description; + + /** + * 根据状态码获取枚举 + */ + public static InvitedStatusEnum getByCode(Integer code) { + if (code == null) { + return null; + } + for (InvitedStatusEnum status : values()) { + if (status.getCode().equals(code)) { + return status; + } + } + return null; + } +} + diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/mapper/ConsultationInvitedMapper.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/mapper/ConsultationInvitedMapper.java new file mode 100644 index 00000000..fd393148 --- /dev/null +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/mapper/ConsultationInvitedMapper.java @@ -0,0 +1,17 @@ +package com.openhis.web.consultation.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.openhis.web.consultation.domain.ConsultationInvited; +import org.apache.ibatis.annotations.Mapper; + +/** + * 会诊邀请对象Mapper接口 + * + * @author system + * @date 2026-01-30 + */ +@Mapper +public interface ConsultationInvitedMapper extends BaseMapper { + +} + diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/mapper/ConsultationRequestMapper.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/mapper/ConsultationRequestMapper.java new file mode 100644 index 00000000..cb7aaaa5 --- /dev/null +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/consultation/mapper/ConsultationRequestMapper.java @@ -0,0 +1,17 @@ +package com.openhis.web.consultation.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.openhis.web.consultation.domain.ConsultationRequest; +import org.apache.ibatis.annotations.Mapper; + +/** + * 会诊申请Mapper接口 + * + * @author system + * @date 2026-01-29 + */ +@Mapper +public interface ConsultationRequestMapper extends BaseMapper { + +} + diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorStationAdviceAppServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorStationAdviceAppServiceImpl.java index f12e7712..58d66b77 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorStationAdviceAppServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorStationAdviceAppServiceImpl.java @@ -864,7 +864,17 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp serviceRequest.setQuantity(adviceSaveDto.getQuantity()); // 请求数量 serviceRequest.setUnitCode(adviceSaveDto.getUnitCode()); // 请求单位编码 - serviceRequest.setCategoryEnum(adviceSaveDto.getCategoryEnum()); // 请求类型 + // 🎯 判断是否为会诊医嘱:如果categoryEnum为31,则为会诊类型 + Integer categoryEnum = adviceSaveDto.getCategoryEnum(); + if (categoryEnum != null && categoryEnum == 31) { + // 会诊医嘱:category_enum设置为31 + serviceRequest.setCategoryEnum(31); + log.info("保存会诊医嘱,category_enum=31"); + } else { + // 普通诊疗医嘱 + serviceRequest.setCategoryEnum(adviceSaveDto.getCategoryEnum()); + } + serviceRequest.setActivityId(adviceSaveDto.getAdviceDefinitionId());// 诊疗定义id serviceRequest.setPatientId(adviceSaveDto.getPatientId()); // 患者 serviceRequest.setRequesterId(adviceSaveDto.getPractitionerId()); // 开方医生 diff --git a/openhis-server-new/openhis-application/src/main/resources/mapper/consultation/ConsultationRequestMapper.xml b/openhis-server-new/openhis-application/src/main/resources/mapper/consultation/ConsultationRequestMapper.xml new file mode 100644 index 00000000..8a104a29 --- /dev/null +++ b/openhis-server-new/openhis-application/src/main/resources/mapper/consultation/ConsultationRequestMapper.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openhis-server-new/openhis-application/src/main/resources/mapper/doctorstation/DoctorStationAdviceAppMapper.xml b/openhis-server-new/openhis-application/src/main/resources/mapper/doctorstation/DoctorStationAdviceAppMapper.xml index a30ee47d..6b7819fc 100644 --- a/openhis-server-new/openhis-application/src/main/resources/mapper/doctorstation/DoctorStationAdviceAppMapper.xml +++ b/openhis-server-new/openhis-application/src/main/resources/mapper/doctorstation/DoctorStationAdviceAppMapper.xml @@ -465,7 +465,8 @@ T2.part_percent AS part_percent, ccd.name AS condition_definition_name, T1.sort_number AS sort_number, - T1.based_on_id AS based_on_id + T1.based_on_id AS based_on_id, + T1.category_enum AS category_enum FROM med_medication_request AS T1 LEFT JOIN med_medication_definition AS T2 ON T2.ID = T1.medication_id AND T2.delete_flag = '0' @@ -516,7 +517,8 @@ T2.part_percent AS part_percent, '' AS condition_definition_name, 99 AS sort_number, - T1.based_on_id AS based_on_id + T1.based_on_id AS based_on_id, + T1.category_enum AS category_enum FROM wor_device_request AS T1 LEFT JOIN adm_device_definition AS T2 ON T2.ID = T1.device_def_id AND T2.delete_flag = '0' @@ -564,7 +566,8 @@ 1 AS part_percent, '' AS condition_definition_name, 99 AS sort_number, - T1.based_on_id AS based_on_id + T1.based_on_id AS based_on_id, + T1.category_enum AS category_enum FROM wor_service_request AS T1 LEFT JOIN wor_activity_definition AS T2 ON T2.ID = T1.activity_id diff --git a/openhis-ui-vue3/src/views/consultationmanagement/consultationconfirmation/index.vue b/openhis-ui-vue3/src/views/consultationmanagement/consultationconfirmation/index.vue new file mode 100644 index 00000000..87b0d6a1 --- /dev/null +++ b/openhis-ui-vue3/src/views/consultationmanagement/consultationconfirmation/index.vue @@ -0,0 +1,402 @@ + + + + + + + + + + + + + + diff --git a/openhis-ui-vue3/src/views/doctorstation/components/adviceBaseList.vue b/openhis-ui-vue3/src/views/doctorstation/components/adviceBaseList.vue index e16acacb..6c8b91a4 100644 --- a/openhis-ui-vue3/src/views/doctorstation/components/adviceBaseList.vue +++ b/openhis-ui-vue3/src/views/doctorstation/components/adviceBaseList.vue @@ -86,7 +86,7 @@ + + diff --git a/openhis-ui-vue3/src/views/doctorstation/components/prescription/prescriptionlist.vue b/openhis-ui-vue3/src/views/doctorstation/components/prescription/prescriptionlist.vue index 3706f18f..944981bd 100644 --- a/openhis-ui-vue3/src/views/doctorstation/components/prescription/prescriptionlist.vue +++ b/openhis-ui-vue3/src/views/doctorstation/components/prescription/prescriptionlist.vue @@ -346,7 +346,7 @@ expandOrder = []; // 当医嘱类型改变时,清空当前选择的项目名称,因为不同类型项目的数据结构可能不兼容 prescriptionList[scope.$index].adviceName = undefined; - adviceQueryParams.adviceType = value; + adviceQueryParams.adviceTypes = value; // 🎯 修复:改为 adviceTypes(复数) // 根据选择的类型设置categoryCode,用于药品分类筛选 if (value == 1) { // 西药 @@ -357,6 +357,8 @@ adviceQueryParams.categoryCode = ''; // 诊疗不需要categoryCode筛选 } else if (value == 4) { // 耗材 adviceQueryParams.categoryCode = ''; // 耗材不需要categoryCode筛选 + } else if (value == 5) { // 会诊 + adviceQueryParams.categoryCode = ''; // 会诊不需要categoryCode筛选 } else { adviceQueryParams.categoryCode = ''; // 全部类型 } @@ -438,6 +440,7 @@ {{ scope.row.chargeStatus_enumText }} + 已作废 已签发 待保存 @@ -569,7 +572,7 @@ import { } from '../api'; import { advicePrint, getAdjustPriceSwitchState } from '@/api/public'; import adviceBaseList from '../adviceBaseList.vue'; -import { computed, getCurrentInstance, nextTick, ref, watch } from 'vue'; +import { computed, getCurrentInstance, nextTick, ref, watch, onMounted, onBeforeUnmount } from 'vue'; import { calculateQuantityByDays } from '@/utils/his'; import OrderGroupDrawer from './orderGroupDrawer'; import PrescriptionHistory from './prescriptionHistory'; @@ -591,7 +594,7 @@ const form = ref({ }); const adviceQueryParams = ref({ searchKey: '', - adviceType: '', + adviceTypes: '', // 🎯 修复:改为 adviceTypes(复数) categoryCode: '' // 用于筛选西药(2)和中成药(1) }); const rowIndex = ref(-1); @@ -664,7 +667,7 @@ const { method_code, unit_code, rate_code, distribution_category_code, drord_doc ); // 删除硬编码的adviceTypeList,直接使用drord_doctor_type字典 -// drord_doctor_type: 1=西药, 2=中成药, 3=诊疗, 4=耗材, 5=全部 +// drord_doctor_type: 1=西药, 2=中成药, 3=诊疗, 4=耗材, 5=会诊, 6=全部 const adviceTypeList = ref([ { label: '西药', @@ -682,6 +685,10 @@ const adviceTypeList = ref([ label: '耗材', value: 4, }, + { + label: '会诊', + value: 5, + }, { label: '全部', value: '', @@ -742,6 +749,8 @@ const allPrescriptionsData = ref({}); // 存储所有处方的数据,格式: { const allPrescriptionCheckStates = ref({}); // 存储每个处方的选中状态,格式: { prescriptionId: { checkedIndexes: [], checkAll: false } } onMounted(() => { document.addEventListener('keydown', escKeyListener); + // 初始化时预加载组织数据,避免选择诊疗/会诊项目时显示"无数据" + getOrgList(); // 初始化时自动创建第一个西药处方 if (westernPrescriptions.value.length === 0) { createNewPrescription(); @@ -1144,7 +1153,7 @@ async function disposalPrint() { function handleTotalAmount() { totalAmount.value = prescriptionList.value.reduce((accumulator, currentRow) => { - if (currentRow.chargeStatus != 8) { + if (currentRow.chargeStatus != 8 && currentRow.statusEnum != 5) { return new Decimal(accumulator).add(currentRow.totalPrice || 0); } else { // 跳过已退费项目,保持累加结果不变 @@ -1183,13 +1192,41 @@ function getListInfo(addNewRow) { // 关键:先等待处方列表数据获取完成 isAdding.value = false; const res = await getPrescriptionList(props.patientInfo.encounterId); + prescriptionList.value = res.data.map((item) => { - return { - ...JSON.parse(item.contentJson), + const contentJson = JSON.parse(item.contentJson); + + // 🎯 判断是否为会诊医嘱: + // 方法1:检查 category_enum 字段(需要后端重新编译) + // 方法2:检查 contentJson 中是否包含会诊相关字段(临时方案) + const categoryEnum = contentJson?.categoryEnum || contentJson?.category_enum || item.category_enum; + const isConsultation = categoryEnum === 31 || categoryEnum === '31' || + contentJson?.consultationType || + contentJson?.consultationId || + contentJson?.consultationRequestId; + + let adviceType = item.adviceType; + let adviceType_dictText = item.adviceType_dictText || mapAdviceTypeLabel(item.adviceType); + + // 如果是会诊类型,设置为会诊类型 + if (isConsultation) { + adviceType = 5; // 前端会诊类型值为 5 + adviceType_dictText = '会诊'; + } + + const result = { + ...contentJson, ...item, - doseQuantity: JSON.parse(item.contentJson)?.doseQuantity, - doseUnitCode_dictText: JSON.parse(item.contentJson)?.doseUnitCode_dictText, + doseQuantity: contentJson?.doseQuantity, + doseUnitCode_dictText: contentJson?.doseUnitCode_dictText, + // 🎯 修复:将 adviceType 和 adviceType_dictText 放在最后,确保不被 item 覆盖 + adviceType: adviceType, + adviceType_dictText: adviceType_dictText, + // 🎯 修复:确保 orgId 被正确设置(从 positionId 映射) + orgId: item.positionId || item.orgId, }; + + return result; }); getGroupMarkers(); // 更新标记 if (props.activeTab == 'prescription' && addNewRow) { @@ -1283,7 +1320,7 @@ function handleAddPrescription(prescriptionId, showWarning = true) { // 重置查询参数 adviceQueryParams.value = { searchKey: '', - adviceType: '', + adviceTypes: '', // 🎯 修复:改为 adviceTypes(复数) categoryCode: '' }; @@ -1363,7 +1400,7 @@ function handleFocus(row, index) { let adviceType = row.adviceType || ''; // 根据医嘱类型设置筛选条件 - // drord_doctor_type: 1=西药, 2=中成药, 3=诊疗, 4=耗材, 5=全部 + // drord_doctor_type: 1=西药, 2=中成药, 3=诊疗, 4=耗材, 5=会诊 if (row.adviceType == 1) { // 西药 categoryCode = '2'; @@ -1380,6 +1417,10 @@ function handleFocus(row, index) { // 耗材:adviceType = 2(后端接口中耗材的adviceType是2) categoryCode = ''; adviceType = 2; // 耗材类型 + } else if (row.adviceType == 5) { + // 🎯 会诊:adviceType = 5 + categoryCode = ''; + adviceType = 5; // 会诊类型 } else { // 全部(5)或其他:显示所有类型 categoryCode = ''; @@ -1387,8 +1428,7 @@ function handleFocus(row, index) { } adviceQueryParams.value = { - adviceType: adviceType, - adviceTypes: adviceType ? adviceType.toString() : '1,2,3', // 根据当前类型设置查询类型,避免显示其他类型的数据 + adviceTypes: adviceType ? adviceType.toString() : '1,2,3', // 🎯 修复:只保留 adviceTypes(复数) categoryCode: categoryCode, searchKey: adviceQueryParams.value.searchKey || '' }; @@ -1566,7 +1606,7 @@ function handleDelete() { handleEmrTreatment(); updateExpandOrder([]); isAdding.value = false; - adviceQueryParams.value.adviceType = undefined; + adviceQueryParams.value.adviceTypes = undefined; // 🎯 修复:改为 adviceTypes(复数) if (sum == selectRows.length) { proxy.$modal.msgSuccess('删除成功'); return; @@ -1732,6 +1772,8 @@ function handleSave(prescriptionId) { saveAdviceType = 2; // 耗材:前端4 -> 后端2 } else if (item.adviceType == 2) { saveAdviceType = 1; // 中成药:前端2 -> 后端1 + } else if (item.adviceType == 5) { + saveAdviceType = 3; // 会诊:前端5 -> 后端3(诊疗类) } // 构造请求参数 @@ -2105,6 +2147,10 @@ function handleSaveSign(row, index, prescriptionId) { else if (params.adviceType == 2) { params.adviceType = 1; } + // 前端5(会诊) -> 后端3 (诊疗类) + else if (params.adviceType == 5) { + params.adviceType = 3; + } // 发送处理后的 params,而不是原始 row savePrescription({ adviceSaveList: [params] }).then((res) => { @@ -2122,7 +2168,7 @@ function handleSaveSign(row, index, prescriptionId) { isAdding.value = false; // --- 修改结束 --- } - adviceQueryParams.value.adviceType = undefined; + adviceQueryParams.value.adviceTypes = undefined; // 🎯 修复:改为 adviceTypes(复数) } }); } @@ -2211,6 +2257,8 @@ function handleSaveBatch(prescriptionId) { saveAdviceType = 2; // 耗材前端4 -> 后端2 } else if (item.adviceType == 2) { saveAdviceType = 1; // 中成药前端2 -> 后端1 + } else if (item.adviceType == 5) { + saveAdviceType = 3; // 会诊前端5 -> 后端3(诊疗类) } // 构造 contentJson (保持前端UI原始数据) @@ -2385,10 +2433,21 @@ function setValue(row) { } } else { getOrgList(); - prescriptionList.value[rowIndex.value].orgId = JSON.parse(JSON.stringify(row)).positionId; - prescriptionList.value[rowIndex.value].quantity = 1; - prescriptionList.value[rowIndex.value].unitPrice = row.priceList[0].price; - prescriptionList.value[rowIndex.value].totalPrice = row.priceList[0].price; + // 会诊类型(adviceType == 5)和诊疗类型(adviceType == 3)的处理 + if (row.adviceType == 5) { + // 会诊类型:设置默认值 + prescriptionList.value[rowIndex.value].orgId = props.patientInfo.orgId; // 执行科室默认为申请医生的科室 + prescriptionList.value[rowIndex.value].quantity = 1; // 执行次数默认1次 + prescriptionList.value[rowIndex.value].unitPrice = row.priceList && row.priceList[0] ? row.priceList[0].price : (row.unitPrice || 0); + prescriptionList.value[rowIndex.value].totalPrice = prescriptionList.value[rowIndex.value].unitPrice; + prescriptionList.value[rowIndex.value].categoryEnum = 31; // 会诊的category_enum设置为31 + } else { + // 诊疗类型(adviceType == 3) + prescriptionList.value[rowIndex.value].orgId = JSON.parse(JSON.stringify(row)).positionId; + prescriptionList.value[rowIndex.value].quantity = 1; + prescriptionList.value[rowIndex.value].unitPrice = row.priceList[0].price; + prescriptionList.value[rowIndex.value].totalPrice = row.priceList[0].price; + } } } diff --git a/openhis-ui-vue3/src/views/doctorstation/index.vue b/openhis-ui-vue3/src/views/doctorstation/index.vue index 27596406..4654789c 100644 --- a/openhis-ui-vue3/src/views/doctorstation/index.vue +++ b/openhis-ui-vue3/src/views/doctorstation/index.vue @@ -169,6 +169,9 @@ + + +
@@ -203,6 +206,7 @@ import { import prescriptionlist from './components/prescription/prescriptionlist.vue'; import RefundListDialog from './components/prescription/refundListDialog.vue'; import ReportQuery from './components/reportQuery.vue'; +import Consultation from './components/consultation.vue'; import PatientList from './components/patientList.vue'; import Diagnosis from './components/diagnosis/diagnosis.vue'; import PrescriptionInfo from './components/prescription/prescriptionInfo.vue'; @@ -294,6 +298,7 @@ const inspectionRef = ref(); const surgeryRef = ref(); const emrRef = ref(); const diagnosisRef = ref(); +const consultationRef = ref(); const waitCount = ref(0); const loading = ref(false); const { proxy } = getCurrentInstance(); @@ -486,6 +491,9 @@ function handleClick(tab) { case 'eprescription': eprescriptionRef.value.getList(); break; + case 'consultation': + consultationRef.value.fetchConsultationList(); + break; } // if (tab != 'emr') { // if (!saveStatus.value) { @@ -586,6 +594,7 @@ function handleCardClick(item, index) { surgeryRef.value.getList(); diagnosisRef.value.getList(); eprescriptionRef.value.getList(); + consultationRef.value.fetchConsultationList(); // emrRef.value.getDetail(item.encounterId); setTimeout(() => { loading.value = false;