feat(surgery): 完善手术管理功能模块

- 添加手术申请相关API接口,包括根据患者ID查询就诊列表功能
- 在医生工作站界面集成手术申请功能选项卡
- 实现手术管理页面的完整功能,包括手术申请的增删改查
- 添加手术排期、开始、完成等状态流转功能
- 优化手术管理页面表格展示,增加手术类型、等级、计划时间等字段
- 实现手术申请表单的完整编辑和查看模式
- 集成患者信息和就诊记录关联功能
- 添加手术室、医生、护士等资源选择功能
- 更新系统依赖配置,添加core-common模块
- 优化图标资源和manifest配置文件
- 调整患者档案和门诊记录相关状态枚举
This commit is contained in:
2026-01-06 16:23:15 +08:00
parent fa2884b320
commit b0850257c8
66 changed files with 7683 additions and 313 deletions

View File

@@ -2,8 +2,11 @@ package com.openhis.web.clinicalmanage.appservice;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.core.common.core.domain.R;
import com.openhis.administration.domain.Encounter;
import com.openhis.web.clinicalmanage.dto.SurgeryDto;
import java.util.List;
/**
* 手术管理应用Service接口
*
@@ -62,4 +65,12 @@ public interface ISurgeryAppService {
* @return 结果
*/
R<?> updateSurgeryStatus(Long id, Integer statusEnum);
/**
* 根据患者ID查询就诊列表
*
* @param patientId 患者ID
* @return 就诊列表
*/
R<List<Encounter>> getEncounterListByPatientId(Long patientId);
}

View File

@@ -1,33 +1,53 @@
package com.openhis.web.clinicalmanage.appservice.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.core.common.core.domain.R;
import com.core.common.core.domain.entity.SysUser;
import com.core.common.utils.MessageUtils;
import com.core.common.utils.SecurityUtils;
import com.core.system.service.ISysUserService;
import com.openhis.administration.domain.ChargeItem;
import com.openhis.administration.domain.Encounter;
import com.openhis.administration.domain.OperatingRoom;
import com.openhis.administration.domain.Organization;
import com.openhis.administration.domain.Patient;
import com.openhis.administration.service.IAccountService;
import com.openhis.administration.service.IChargeItemService;
import com.openhis.administration.service.IEncounterService;
import com.openhis.administration.service.IOrganizationService;
import com.openhis.administration.service.IOperatingRoomService;
import com.openhis.administration.service.IPatientService;
import com.openhis.common.constant.PromptMsgConstant;
import com.openhis.clinical.domain.Surgery;
import com.openhis.clinical.service.ISurgeryService;
import com.openhis.common.constant.CommonConstants;
import com.openhis.common.constant.PromptMsgConstant;
import com.openhis.common.enums.ChargeItemStatus;
import com.openhis.common.enums.GenerateSource;
import com.openhis.common.enums.RequestStatus;
import com.openhis.common.enums.TherapyTimeType;
import com.openhis.common.utils.HisQueryUtils;
import com.openhis.document.domain.RequestForm;
import com.openhis.document.service.IRequestFormService;
import com.openhis.web.clinicalmanage.appservice.ISurgeryAppService;
import com.openhis.web.clinicalmanage.dto.SurgeryDto;
import com.openhis.web.clinicalmanage.mapper.SurgeryAppMapper;
import com.openhis.workflow.domain.ServiceRequest;
import com.openhis.workflow.service.IActivityDefinitionService;
import com.openhis.workflow.service.IServiceRequestService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import static com.core.framework.datasource.DynamicDataSourceContextHolder.log;
/**
* 手术管理应用Service业务层处理
*
* @author system
* @date 2025-12-30
*/
@Service
public class SurgeryAppServiceImpl implements ISurgeryAppService {
@@ -40,6 +60,33 @@ public class SurgeryAppServiceImpl implements ISurgeryAppService {
@Resource
private IPatientService patientService;
@Resource
private IEncounterService encounterService;
@Resource
private IRequestFormService requestFormService;
@Resource
private IServiceRequestService serviceRequestService;
@Resource
private IChargeItemService chargeItemService;
@Resource
private IActivityDefinitionService activityDefinitionService;
@Resource
private IAccountService accountService;
@Resource
private IOrganizationService organizationService;
@Resource
private ISysUserService sysUserService;
@Resource
private IOperatingRoomService operatingRoomService;
/**
* 分页查询手术列表
*
@@ -85,6 +132,7 @@ public class SurgeryAppServiceImpl implements ISurgeryAppService {
* @return 结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public R<?> addSurgery(SurgeryDto surgeryDto) {
// 校验患者是否存在
Patient patient = patientService.getById(surgeryDto.getPatientId());
@@ -92,14 +140,169 @@ public class SurgeryAppServiceImpl implements ISurgeryAppService {
return R.fail("患者信息不存在");
}
// 校验就诊ID是否存在
if (surgeryDto.getEncounterId() == null) {
return R.fail("请选择就诊流水号");
}
// 校验就诊记录是否存在
Encounter encounter = encounterService.getById(surgeryDto.getEncounterId());
if (encounter == null) {
return R.fail("就诊记录不存在");
}
// 获取患者的自费账户ID
Long accountId = accountService.getSelfPayAccount(surgeryDto.getEncounterId());
if (accountId == null) {
return R.fail("未找到患者的账户信息,请先完成挂号或住院登记");
}
// 当前登录账号的科室id
Long orgId = SecurityUtils.getLoginUser().getOrgId();
// 当前参与者ID
Long practitionerId = SecurityUtils.getLoginUser().getPractitionerId();
// 当前用户ID
Long userId = SecurityUtils.getLoginUser().getUserId();
// 当前时间
Date curDate = new Date();
// 获取申请医生姓名(从当前登录用户信息中获取)
String applyDoctorName = SecurityUtils.getLoginUser().getUser().getNickName();
// 获取申请科室名称
String applyDeptName = null;
// 优先从用户信息的部门中获取
if (SecurityUtils.getLoginUser().getUser().getDept() != null) {
applyDeptName = SecurityUtils.getLoginUser().getUser().getDept().getDeptName();
}
// 如果用户信息中没有部门名称,则从机构表中查询
if (applyDeptName == null && orgId != null) {
Organization org = organizationService.getById(orgId);
if (org != null) {
applyDeptName = org.getName();
}
}
// 转换为实体对象
Surgery surgery = new Surgery();
BeanUtils.copyProperties(surgeryDto, surgery);
// 清空名称字段确保从ID重新查询并填充
surgery.setPatientName(null);
surgery.setMainSurgeonName(null);
surgery.setAnesthetistName(null);
surgery.setAssistant1Name(null);
surgery.setAssistant2Name(null);
surgery.setScrubNurseName(null);
surgery.setOperatingRoomName(null);
surgery.setOrgName(null);
surgery.setApplyDoctorName(null);
surgery.setApplyDeptName(null);
// 设置申请医生信息(默认使用当前登录医生)
// 注意:必须放在 copyProperties 之后,确保覆盖前端可能传递的空值
log.info("设置申请医生信息 - doctorId: {}, doctorName: {}, deptId: {}, deptName: {}",
practitionerId, applyDoctorName, orgId, applyDeptName);
log.info("前端提交的数据 - applyDoctorId: {}, applyDoctorName: {}, applyDeptId: {}, applyDeptName: {}",
surgeryDto.getApplyDoctorId(), surgeryDto.getApplyDoctorName(), surgeryDto.getApplyDeptId(), surgeryDto.getApplyDeptName());
surgery.setApplyDoctorId(practitionerId);
surgery.setApplyDoctorName(applyDoctorName);
surgery.setApplyDeptId(orgId);
surgery.setApplyDeptName(applyDeptName);
// 填充其他人员字段的名称
fillSurgeryNameFields(surgery);
// 设置创建者ID因为数据库中 create_by 是 bigint 类型)
// 这个值会被 MybastisColumnsHandler 自动填充,所以这里不需要设置
log.info("准备插入手术记录 - applyDoctorId: {}, applyDoctorName: {}, applyDeptId: {}, applyDeptName: {}",
surgery.getApplyDoctorId(), surgery.getApplyDoctorName(), surgery.getApplyDeptId(), surgery.getApplyDeptName());
log.info("准备插入手术记录 - mainSurgeonId: {}, mainSurgeonName: {}, anesthetistId: {}, anesthetistName: {}",
surgery.getMainSurgeonId(), surgery.getMainSurgeonName(), surgery.getAnesthetistId(), surgery.getAnesthetistName());
log.info("准备插入手术记录 - assistant1Id: {}, assistant1Name: {}, assistant2Id: {}, assistant2Name: {}",
surgery.getAssistant1Id(), surgery.getAssistant1Name(), surgery.getAssistant2Id(), surgery.getAssistant2Name());
log.info("准备插入手术记录 - operatingRoomId: {}, operatingRoomName: {}, orgId: {}, orgName: {}",
surgery.getOperatingRoomId(), surgery.getOperatingRoomName(), surgery.getOrgId(), surgery.getOrgName());
Long surgeryId = surgeryService.insertSurgery(surgery);
log.info("手术记录插入成功 - surgeryId: {}, surgeryNo: {}", surgeryId, surgery.getSurgeryNo());
// 生成处方号(医嘱号)
String prescriptionNo = surgery.getSurgeryNo();
// 保存申请单
RequestForm requestForm = new RequestForm();
requestForm.setTypeCode("SURGERY"); // 申请单类型
requestForm.setPrescriptionNo(prescriptionNo); // 处方号(使用手术单号)
requestForm.setName("手术申请单"); // 名称
requestForm.setEncounterId(surgeryDto.getEncounterId()); // 就诊ID
requestForm.setRequesterId(practitionerId); // 申请人
requestForm.setDescJson(buildDescJson(surgeryDto)); // 描述内容
requestFormService.save(requestForm);
// 生成手术医嘱
ServiceRequest serviceRequest = new ServiceRequest();
serviceRequest.setStatusEnum(RequestStatus.DRAFT.getValue());
serviceRequest.setBusNo(String.format("%04d", (int) (Math.random() * 10000)));
serviceRequest.setGenerateSourceEnum(GenerateSource.DOCTOR_PRESCRIPTION.getValue()); // 生成来源
serviceRequest.setPrescriptionNo(prescriptionNo);
serviceRequest.setTherapyEnum(TherapyTimeType.TEMPORARY.getValue());// 治疗类型
serviceRequest.setQuantity(BigDecimal.valueOf(1)); // 请求数量
serviceRequest.setUnitCode(""); // 请求单位编码
serviceRequest.setCategoryEnum(4); // 请求类型4-手术
serviceRequest.setActivityId(surgeryId); // 手术ID作为诊疗定义id
serviceRequest.setPatientId(surgeryDto.getPatientId()); // 患者
serviceRequest.setRequesterId(practitionerId); // 开方医生
serviceRequest.setEncounterId(surgeryDto.getEncounterId()); // 就诊id
serviceRequest.setAuthoredTime(curDate); // 请求签发时间
serviceRequest.setOrgId(orgId); // 执行科室
serviceRequestService.save(serviceRequest);
// 生成收费项目
ChargeItem chargeItem = new ChargeItem();
chargeItem.setStatusEnum(ChargeItemStatus.DRAFT.getValue()); // 收费状态
chargeItem.setBusNo("CI" + serviceRequest.getBusNo());
chargeItem.setGenerateSourceEnum(GenerateSource.DOCTOR_PRESCRIPTION.getValue()); // 生成来源
chargeItem.setPatientId(surgeryDto.getPatientId()); // 患者
chargeItem.setContextEnum(3); // 类型3-诊疗
chargeItem.setEncounterId(surgeryDto.getEncounterId()); // 就诊id
chargeItem.setAccountId(accountId); // 账户ID
chargeItem.setDefinitionId(surgeryId); // 手术ID作为费用定价ID
chargeItem.setEntererId(practitionerId);// 开立人ID
chargeItem.setEnteredDate(curDate); // 开立时间
chargeItem.setServiceTable(CommonConstants.TableName.WOR_SERVICE_REQUEST);// 医疗服务类型
chargeItem.setServiceId(serviceRequest.getId()); // 医疗服务ID
chargeItem.setProductTable("cli_surgery");// 手术表
chargeItem.setProductId(surgeryId);// 手术ID作为收费项id
chargeItem.setRequestingOrgId(orgId); // 开立科室
chargeItem.setQuantityValue(BigDecimal.valueOf(1)); // 数量
chargeItem.setQuantityUnit(""); // 单位
chargeItem.setUnitPrice(surgeryDto.getSurgeryFee() != null ? surgeryDto.getSurgeryFee() : new BigDecimal("0.0")); // 单价
chargeItem.setTotalPrice(surgeryDto.getTotalFee() != null ? surgeryDto.getTotalFee() : new BigDecimal("0.0")); // 总价
chargeItemService.save(chargeItem);
return R.ok(surgeryId, MessageUtils.createMessage(PromptMsgConstant.Common.M00001, new Object[]{"手术信息"}));
}
/**
* 构建描述JSON
*
* @param surgeryDto 手术信息
* @return JSON字符串
*/
private String buildDescJson(SurgeryDto surgeryDto) {
return String.format(
"{\"surgeryName\":\"%s\",\"surgeryLevel\":\"%s\",\"surgeryIndication\":\"%s\",\"preoperativeDiagnosis\":\"%s\"}",
surgeryDto.getSurgeryName() != null ? surgeryDto.getSurgeryName() : "",
surgeryDto.getSurgeryLevel() != null ? surgeryDto.getSurgeryLevel() : "",
surgeryDto.getSurgeryIndication() != null ? surgeryDto.getSurgeryIndication() : "",
surgeryDto.getPreoperativeDiagnosis() != null ? surgeryDto.getPreoperativeDiagnosis() : ""
);
}
/**
* 修改手术信息
*
@@ -117,7 +320,22 @@ public class SurgeryAppServiceImpl implements ISurgeryAppService {
// 转换为实体对象
Surgery surgery = new Surgery();
BeanUtils.copyProperties(surgeryDto, surgery);
// 先清空名称字段,确保重新填充
surgery.setPatientName(null);
surgery.setMainSurgeonName(null);
surgery.setAnesthetistName(null);
surgery.setAssistant1Name(null);
surgery.setAssistant2Name(null);
surgery.setScrubNurseName(null);
surgery.setOperatingRoomName(null);
surgery.setOrgName(null);
surgery.setApplyDoctorName(null);
surgery.setApplyDeptName(null);
// 填充其他人员字段的名称
fillSurgeryNameFields(surgery);
surgeryService.updateSurgery(surgery);
return R.ok(null, MessageUtils.createMessage(PromptMsgConstant.Common.M00002, new Object[]{"手术信息"}));
}
@@ -163,4 +381,124 @@ public class SurgeryAppServiceImpl implements ISurgeryAppService {
surgeryService.updateSurgeryStatus(id, statusEnum);
return R.ok(null, MessageUtils.createMessage(PromptMsgConstant.Common.M00004, new Object[]{"手术状态"}));
}
}
/**
* 根据患者ID查询就诊列表
*
* @param patientId 患者ID
* @return 就诊列表
*/
@Override
public R<List<Encounter>> getEncounterListByPatientId(Long patientId) {
if (patientId == null) {
return R.fail("患者ID不能为空");
}
// 查询该患者的所有就诊记录(进行中的优先)
QueryWrapper<Encounter> wrapper = new QueryWrapper<>();
wrapper.eq("patient_id", patientId)
.in("status_enum", 2, 3) // 2-进行中, 3-已完成
.orderByAsc("CASE WHEN status_enum = 2 THEN 0 ELSE 1 END") // 进行中的排在前面
.orderByDesc("start_time"); // 按开始时间倒序
List<Encounter> encounterList = encounterService.list(wrapper);
if (encounterList == null || encounterList.isEmpty()) {
return R.fail("该患者暂无就诊记录,请先挂号或办理住院");
}
return R.ok(encounterList);
}
/**
* 填充手术记录中的名称字段
* 根据ID反向查询用户表、机构表、手术室表、患者表、就诊表,填充对应的名称字段
*
* @param surgery 手术实体对象
*/
private void fillSurgeryNameFields(Surgery surgery) {
// 填充患者姓名
if (surgery.getPatientId() != null) {
Patient patient = patientService.getById(surgery.getPatientId());
if (patient != null) {
surgery.setPatientName(patient.getName());
}
}
// 填充主刀医生姓名
if (surgery.getMainSurgeonId() != null) {
SysUser mainSurgeon = sysUserService.selectUserById(surgery.getMainSurgeonId());
if (mainSurgeon != null) {
surgery.setMainSurgeonName(mainSurgeon.getNickName());
}
}
// 填充麻醉医生姓名
if (surgery.getAnesthetistId() != null) {
SysUser anesthetist = sysUserService.selectUserById(surgery.getAnesthetistId());
if (anesthetist != null) {
surgery.setAnesthetistName(anesthetist.getNickName());
}
}
// 填充助手1姓名
if (surgery.getAssistant1Id() != null) {
SysUser assistant1 = sysUserService.selectUserById(surgery.getAssistant1Id());
if (assistant1 != null) {
surgery.setAssistant1Name(assistant1.getNickName());
}
}
// 填充助手2姓名
if (surgery.getAssistant2Id() != null) {
SysUser assistant2 = sysUserService.selectUserById(surgery.getAssistant2Id());
if (assistant2 != null) {
surgery.setAssistant2Name(assistant2.getNickName());
}
}
// 填充巡回护士姓名
if (surgery.getScrubNurseId() != null) {
SysUser scrubNurse = sysUserService.selectUserById(surgery.getScrubNurseId());
if (scrubNurse != null) {
surgery.setScrubNurseName(scrubNurse.getNickName());
}
}
// 填充手术室名称
if (surgery.getOperatingRoomId() != null) {
OperatingRoom operatingRoom = operatingRoomService.getById(surgery.getOperatingRoomId());
if (operatingRoom != null) {
surgery.setOperatingRoomName(operatingRoom.getName());
}
}
// 填充执行科室名称
if (surgery.getOrgId() != null) {
Organization org = organizationService.getById(surgery.getOrgId());
if (org != null) {
surgery.setOrgName(org.getName());
}
}
// 填充申请科室名称(如果还没有设置)
if (surgery.getApplyDeptId() != null && (surgery.getApplyDeptName() == null || surgery.getApplyDeptName().isEmpty())) {
Organization applyDept = organizationService.getById(surgery.getApplyDeptId());
if (applyDept != null) {
surgery.setApplyDeptName(applyDept.getName());
}
}
// 填充申请医生姓名(如果还没有设置)
if (surgery.getApplyDoctorId() != null && (surgery.getApplyDoctorName() == null || surgery.getApplyDoctorName().isEmpty())) {
SysUser applyDoctor = sysUserService.selectUserById(surgery.getApplyDoctorId());
if (applyDoctor != null) {
surgery.setApplyDoctorName(applyDoctor.getNickName());
}
}
log.info("填充手术名称字段完成 - patientName: {}, mainSurgeonName: {}, anesthetistName: {}, assistant1Name: {}, assistant2Name: {}, scrubNurseName: {}, operatingRoomName: {}, orgName: {}",
surgery.getPatientName(), surgery.getMainSurgeonName(), surgery.getAnesthetistName(), surgery.getAssistant1Name(),
surgery.getAssistant2Name(), surgery.getScrubNurseName(), surgery.getOperatingRoomName(), surgery.getOrgName());
}
}

View File

@@ -2,12 +2,15 @@ package com.openhis.web.clinicalmanage.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.core.common.core.domain.R;
import com.openhis.administration.domain.Encounter;
import com.openhis.web.clinicalmanage.appservice.ISurgeryAppService;
import com.openhis.web.clinicalmanage.dto.SurgeryDto;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 手术管理Controller业务层处理
*
@@ -93,4 +96,15 @@ public class SurgeryController {
public R<?> updateSurgeryStatus(@RequestParam Long id, @RequestParam Integer statusEnum) {
return surgeryAppService.updateSurgeryStatus(id, statusEnum);
}
/**
* 根据患者ID查询就诊列表
*
* @param patientId 患者ID
* @return 就诊列表
*/
@GetMapping(value = "/encounter-list")
public R<List<Encounter>> getEncounterListByPatientId(@RequestParam Long patientId) {
return surgeryAppService.getEncounterListByPatientId(patientId);
}
}

View File

@@ -43,6 +43,23 @@ public class SurgeryDto {
/** 就诊流水号 */
private String encounterNo;
/** 申请医生ID */
@JsonSerialize(using = ToStringSerializer.class)
private Long applyDoctorId;
/** 申请医生姓名 */
private String applyDoctorName;
/** 申请科室ID */
@JsonSerialize(using = ToStringSerializer.class)
private Long applyDeptId;
/** 申请科室名称 */
private String applyDeptName;
/** 手术指征 */
private String surgeryIndication;
/** 手术名称 */
private String surgeryName;
@@ -133,6 +150,13 @@ public class SurgeryDto {
/** 手术室名称 */
private String operatingRoomName;
/** 手术室所属机构ID */
@JsonSerialize(using = ToStringSerializer.class)
private Long operatingRoomOrgId;
/** 手术室所属机构名称 */
private String operatingRoomOrgName;
/** 执行科室ID */
@JsonSerialize(using = ToStringSerializer.class)
private Long orgId;
@@ -172,4 +196,4 @@ public class SurgeryDto {
/** 更新时间 */
private Date updateTime;
}
}