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 f1db8bfea..40bd312e9 100755 --- 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 @@ -35,6 +35,9 @@ import com.openhis.medication.service.IMedicationDispenseService; import com.openhis.medication.service.IMedicationRequestService; import com.openhis.web.chargemanage.mapper.OutpatientRegistrationAppMapper; import com.openhis.web.doctorstation.appservice.IDoctorStationAdviceAppService; +import com.openhis.document.service.IRequestFormService; +import com.openhis.clinical.service.ISurgeryService; +import com.openhis.clinical.domain.Surgery; import com.openhis.web.doctorstation.appservice.IDoctorStationInspectionLabApplyService; import com.openhis.web.doctorstation.dto.*; import com.openhis.web.doctorstation.mapper.DoctorStationAdviceAppMapper; @@ -51,6 +54,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import com.openhis.document.domain.RequestForm; import javax.annotation.Resource; import java.math.BigDecimal; @@ -69,6 +73,7 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp private static final Pattern INSPECTION_APPLY_NO_JSON = Pattern.compile("\"applyNo\"\\s*:\\s*\"([^\"]+)\""); + @Resource AssignSeqUtil assignSeqUtil; @@ -132,6 +137,20 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp @Lazy private IDoctorStationInspectionLabApplyService iDoctorStationInspectionLabApplyService; + /** + * 与RequestFormManageAppServiceImpl存在循环依赖,需延迟注入;删除手术医嘱时级联作废手术申请单。 + */ + @Resource + @Lazy + private IRequestFormService iRequestFormService; + + /** + * 删除手术医嘱时级联删除 cli_surgery 手术记录。 + */ + @Resource + @Lazy + private ISurgeryService iSurgeryService; + // 缓存 key 前缀 private static final String ADVICE_BASE_INFO_CACHE_PREFIX = "advice:base:info:"; // 缓存过期时间(小时) @@ -1750,6 +1769,7 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp return StringUtils.isBlank(applyNo) ? null : applyNo; } + /** * 处理诊疗 */ @@ -1798,31 +1818,50 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp } } } - // 检验申请单在医嘱 contentJson 中写入 applyNo;从医嘱删除时需先级联作废检验单,避免检验页签仍显示孤儿申请 + // 🔧 级联作废:在删除 ServiceRequest 之前,先读取所有待删除记录的级联信息 + // 检验申请单:contentJson 中写入 applyNo;手术申请单:categoryEnum=24 + prescriptionNo Map> labApplyNoToRequestIds = new LinkedHashMap<>(); + Map> surgeryPrescriptionNoToRequestIds = new LinkedHashMap<>(); + // 收集待删除的 ServiceRequest(先查询再删除,避免级联逻辑因记录已删除而失效) + Map serviceRequestCache = new LinkedHashMap<>(); for (AdviceSaveDto adviceSaveDto : deleteList) { Long requestId = adviceSaveDto.getRequestId(); - // 🔧 Bug #442: 跳过 requestId 为 null 的记录,避免删除不存在的诊疗请求 + // 🔧 Bug #442: 跳过 requestId 为 null 的记录 if (requestId == null) { log.warn("BugFix#442: handService - 跳过 requestId 为 null 的删除请求"); continue; } - iServiceRequestService.removeById(requestId);// 删除诊疗 - ServiceRequest existing = iServiceRequestService.getById(adviceSaveDto.getRequestId()); + ServiceRequest existing = iServiceRequestService.getById(requestId); if (existing == null) { continue; } + serviceRequestCache.put(requestId, existing); + log.info("【调试】handService 待删除医嘱: requestId={}, categoryEnum={}, prescriptionNo={}", + requestId, existing.getCategoryEnum(), existing.getPrescriptionNo()); + // 检验申请单级联 String applyNo = extractInspectionApplyNoFromContentJson(existing.getContentJson()); if (StringUtils.isNotBlank(applyNo)) { labApplyNoToRequestIds.computeIfAbsent(applyNo, k -> new ArrayList<>()) - .add(adviceSaveDto.getRequestId()); + .add(requestId); + } + // 手术申请单级联(categoryEnum=24) + log.info("【调试】handService 判断手术条件: categoryEnum={}, prescriptionNo={}, isSurgery={}", + existing.getCategoryEnum(), existing.getPrescriptionNo(), + existing.getCategoryEnum() != null && existing.getCategoryEnum() == 24 && StringUtils.isNotBlank(existing.getPrescriptionNo())); + if (existing.getCategoryEnum() != null + && existing.getCategoryEnum() == 24 + && StringUtils.isNotBlank(existing.getPrescriptionNo())) { + surgeryPrescriptionNoToRequestIds.computeIfAbsent(existing.getPrescriptionNo(), k -> new ArrayList<>()) + .add(requestId); + log.info("【调试】handService 加入手术级联列表: prescriptionNo={}", existing.getPrescriptionNo()); } } - Set labCascadeSkippedRequestIds = new HashSet<>(); + // 执行检验申请单级联作废 + Set cascadeSkippedRequestIds = new HashSet<>(); for (Map.Entry> e : labApplyNoToRequestIds.entrySet()) { R delLab = iDoctorStationInspectionLabApplyService.deleteInspectionLabApply(e.getKey()); if (delLab != null && R.isSuccess(delLab)) { - labCascadeSkippedRequestIds.addAll(e.getValue()); + cascadeSkippedRequestIds.addAll(e.getValue()); log.info("handService - 级联作废检验申请单 applyNo={},已跳过重复删除的医嘱 requestIds={}", e.getKey(), e.getValue()); } else { @@ -1831,8 +1870,41 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp e.getKey(), msg); } } + // 🔧 手术申请单级联作废:删除手术医嘱时同步作废关联的手术申请单 + for (Map.Entry> e : surgeryPrescriptionNoToRequestIds.entrySet()) { + String prescriptionNo = e.getKey(); + try { + List requestForms = iRequestFormService.list( + new LambdaQueryWrapper() + .eq(RequestForm::getPrescriptionNo, prescriptionNo) + .in(RequestForm::getTypeCode, ActivityDefCategory.PROCEDURE.getCode().toString(), "SURGERY") + .and(w -> w.isNull(RequestForm::getDeleteFlag).or().eq(RequestForm::getDeleteFlag, "0"))); + log.info("【调试】handService 查询手术申请单: prescriptionNo={}, 查到{}条", prescriptionNo, requestForms != null ? requestForms.size() : 0); + if (requestForms != null && !requestForms.isEmpty()) { + for (RequestForm requestForm : requestForms) { + iRequestFormService.removeById(requestForm.getId()); + } + // 同步删除 cli_surgery 手术记录(prescriptionNo = surgeryNo) + Surgery surgery = iSurgeryService.getOne( + new LambdaQueryWrapper() + .eq(Surgery::getSurgeryNo, prescriptionNo) + .and(w -> w.isNull(Surgery::getDeleteFlag).or().eq(Surgery::getDeleteFlag, "0"))); + if (surgery != null) { + iSurgeryService.removeById(surgery.getId()); + log.info("handService - 级联删除手术记录 cli_surgery: surgeryNo={}, id={}", prescriptionNo, surgery.getId()); + } + cascadeSkippedRequestIds.addAll(e.getValue()); + log.info("handService - 级联作废手术申请单 prescriptionNo={}", prescriptionNo); + } else { + log.info("handService - 未找到手术申请单 prescriptionNo={}", prescriptionNo); + } + } catch (Exception ex) { + log.warn("handService - 级联作废手术申请单失败 prescriptionNo={} msg={}", prescriptionNo, ex.getMessage()); + } + } + // 级联作废完成后,统一删除 ServiceRequest 及其子项、费用项 for (AdviceSaveDto adviceSaveDto : deleteList) { - if (labCascadeSkippedRequestIds.contains(adviceSaveDto.getRequestId())) { + if (cascadeSkippedRequestIds.contains(adviceSaveDto.getRequestId())) { continue; } Long requestId = adviceSaveDto.getRequestId(); @@ -1842,7 +1914,6 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp requestId));// 删除诊疗套餐对应的子项 // 🔧 Bug Fix #219: 删除费用项 String serviceTable = CommonConstants.TableName.WOR_SERVICE_REQUEST; - // 直接删除费用项 iChargeItemService.deleteByServiceTableAndId(serviceTable, requestId); log.info("BugFix#219: 诊疗医嘱删除完成, requestId={}, serviceTable={}", requestId, serviceTable); } @@ -2481,21 +2552,17 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp } } - // 使用直接 LIMIT 查询,不触发 MyBatis Plus 的 COUNT 开销 - List records = doctorStationAdviceAppMapper.getSurgeryPage( + // 使用 MyBatis Plus 分页查询 + IPage result = doctorStationAdviceAppMapper.getSurgeryPage( new Page<>(pageNo, pageSize), PublicationStatus.ACTIVE.getValue(), organizationId, searchKey); - // 手动构造 Page 对象,total 设为 records.size()(前端 el-transfer 不需要精确的 total 总数) - IPage result = new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(pageNo, pageSize); - result.setRecords(records); - result.setTotal(records.size()); log.info("getSurgeryPage 完成: {}ms, total={}, records={}", System.currentTimeMillis() - start, result.getTotal(), result.getRecords().size()); // 无搜索时将结果写入缓存 - if (useCache) { + if (useCache && result instanceof com.baomidou.mybatisplus.extension.plugins.pagination.Page) { redisCache.setCacheObject(cacheKey, result, (int) CACHE_EXPIRE_HOURS, java.util.concurrent.TimeUnit.HOURS); log.info("缓存手术项目, key: {}, 过期时间: {} 小时", cacheKey, CACHE_EXPIRE_HOURS); } diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/mapper/DoctorStationAdviceAppMapper.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/mapper/DoctorStationAdviceAppMapper.java index 13fd8d510..67a16f4b8 100755 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/mapper/DoctorStationAdviceAppMapper.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/mapper/DoctorStationAdviceAppMapper.java @@ -195,7 +195,7 @@ public interface DoctorStationAdviceAppMapper { * @param searchKey 模糊查询关键字(可选) * @return 手术项目分页数据 */ - List getSurgeryPage(@Param("page") Page page, + IPage getSurgeryPage(@Param("page") Page page, @Param("statusEnum") Integer statusEnum, @Param("organizationId") Long organizationId, @Param("searchKey") String searchKey); diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/inventorymanage/appservice/impl/ProductTransferAppServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/inventorymanage/appservice/impl/ProductTransferAppServiceImpl.java index cb68c6787..c1e644e96 100755 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/inventorymanage/appservice/impl/ProductTransferAppServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/inventorymanage/appservice/impl/ProductTransferAppServiceImpl.java @@ -32,6 +32,7 @@ import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.math.BigDecimal; import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -202,62 +203,70 @@ public class ProductTransferAppServiceImpl implements IProductTransferAppService @Override public R addOrEditBatchTransferReceipt(List productTransferDtoList, Boolean flag) { - // 校验调拨数量:必须 > 0 且不超过源库存数量(从数据库查实时库存) Integer tenantId = SecurityUtils.getLoginUser().getTenantId(); - for (ProductTransferDto dto : productTransferDtoList) { - if (dto.getItemQuantity() == null || dto.getItemQuantity().compareTo(java.math.BigDecimal.ZERO) <= 0) { - return R.fail("调拨数量必须大于0"); - } - // 查询该药品在源仓库的实时库存总量 - List inventoryList = inventoryItemService.selectInventoryByItemId( - dto.getItemId(), dto.getLotNumber(), dto.getSourceLocationId(), tenantId); - java.math.BigDecimal actualStock = inventoryList.stream() - .map(InventoryItem::getQuantity) - .reduce(java.math.BigDecimal.ZERO, java.math.BigDecimal::add); - if (dto.getItemQuantity().compareTo(actualStock) > 0) { - return R.fail("调拨数量不可超出源库存数量(当前库存:" + actualStock + ")"); - } - } + Date now = DateUtils.getNowDate(); List idList = new ArrayList<>(); if (flag) { // 批量保存按钮 - // 单据号取得 List busNoList = productTransferDtoList.stream().map(ProductTransferDto::getBusNo).toList(); - // 请求id取得 - List requestList = supplyRequestService.getSupplyByBusNo(busNoList.get(0)); - if (!requestList.isEmpty()) { - List requestIdList = requestList.stream().map(SupplyRequest::getId).collect(Collectors.toList()); - // 单据信息删除 - supplyRequestService.removeByIds(requestIdList); + // 保存前:获取旧记录用于恢复预划扣库存 + List oldRequestList = supplyRequestService.getSupplyByBusNo(busNoList.get(0)); + if (!oldRequestList.isEmpty()) { + // 恢复旧记录已预划扣的库存 + for (SupplyRequest oldReq : oldRequestList) { + if (oldReq.getItemId() != null && oldReq.getLotNumber() != null + && oldReq.getSourceLocationId() != null && oldReq.getItemQuantity() != null) { + List invList = inventoryItemService.selectInventoryByItemId( + oldReq.getItemId(), oldReq.getLotNumber(), oldReq.getSourceLocationId(), tenantId); + if (!invList.isEmpty()) { + inventoryItemService.updateInventoryQuantity( + invList.get(0).getId(), + invList.get(0).getQuantity().add(oldReq.getItemQuantity()), now); + } + } + } + List oldIdList = oldRequestList.stream().map(SupplyRequest::getId).collect(Collectors.toList()); + supplyRequestService.removeByIds(oldIdList); + } + + // 校验 + 预划扣新记录 + for (ProductTransferDto dto : productTransferDtoList) { + if (dto.getItemQuantity() == null || dto.getItemQuantity().compareTo(BigDecimal.ZERO) <= 0) { + return R.fail("调拨数量必须大于0"); + } + List inventoryList = inventoryItemService.selectInventoryByItemId( + dto.getItemId(), dto.getLotNumber(), dto.getSourceLocationId(), tenantId); + BigDecimal actualStock = inventoryList.stream() + .map(InventoryItem::getQuantity) + .reduce(BigDecimal.ZERO, BigDecimal::add); + if (dto.getItemQuantity().compareTo(actualStock) > 0) { + return R.fail("调拨数量不可超出源库存数量(当前库存:" + actualStock + ")"); + } + // 预划扣源仓库库存 + if (!inventoryList.isEmpty()) { + InventoryItem inv = inventoryList.get(0); + inventoryItemService.updateInventoryQuantity( + inv.getId(), inv.getQuantity().subtract(dto.getItemQuantity()), now); + } } // 生成批量调拨单据 List supplyRequestList = new ArrayList<>(); for (ProductTransferDto productTransferDto : productTransferDtoList) { - // 初始化单据信息 SupplyRequest supplyRequest = new SupplyRequest(); BeanUtils.copyProperties(productTransferDto, supplyRequest); - // 生成商品批量调拨单据 supplyRequest - // id .setId(null) - // 单据分类:库存供应 .setCategoryEnum(SupplyCategory.STOCK_SUPPLY.getValue()) - // 单据类型:商品批量调拨 .setTypeEnum(SupplyType.PRODUCT_BATCH_TRANSFER.getValue()) - // 制单人 .setApplicantId(SecurityUtils.getLoginUser().getPractitionerId()) - // 申请时间 .setApplyTime(DateUtils.getNowDate()) - // 源库存数量 .setTotalQuantity(productTransferDto.getTotalSourceQuantity()); supplyRequestList.add(supplyRequest); } supplyRequestService.saveOrUpdateBatch(supplyRequestList); - // 请求id取得 List supplyRequestIdList = supplyRequestService.getSupplyByBusNo(busNoList.get(0)); - // 返回请求id列表 List requestIdList = supplyRequestIdList.stream().map(SupplyRequest::getId).toList(); for (Long list : requestIdList) { idList.add(list.toString()); @@ -265,33 +274,58 @@ public class ProductTransferAppServiceImpl implements IProductTransferAppService } else { // 单独保存按钮 for (ProductTransferDto productTransferDto : productTransferDtoList) { - // 初始化单据信息 + // 更新已有记录:先恢复旧预划扣,再扣新的 + if (productTransferDto.getId() != null) { + SupplyRequest oldReq = supplyRequestService.getById(productTransferDto.getId()); + if (oldReq != null && oldReq.getItemId() != null && oldReq.getLotNumber() != null + && oldReq.getSourceLocationId() != null && oldReq.getItemQuantity() != null) { + List invList = inventoryItemService.selectInventoryByItemId( + oldReq.getItemId(), oldReq.getLotNumber(), oldReq.getSourceLocationId(), tenantId); + if (!invList.isEmpty()) { + inventoryItemService.updateInventoryQuantity( + invList.get(0).getId(), + invList.get(0).getQuantity().add(oldReq.getItemQuantity()), now); + } + } + } + + // 校验 + 预划扣 + if (productTransferDto.getItemQuantity() == null + || productTransferDto.getItemQuantity().compareTo(BigDecimal.ZERO) <= 0) { + return R.fail("调拨数量必须大于0"); + } + List inventoryList = inventoryItemService.selectInventoryByItemId( + productTransferDto.getItemId(), productTransferDto.getLotNumber(), + productTransferDto.getSourceLocationId(), tenantId); + BigDecimal actualStock = inventoryList.stream() + .map(InventoryItem::getQuantity) + .reduce(BigDecimal.ZERO, BigDecimal::add); + if (productTransferDto.getItemQuantity().compareTo(actualStock) > 0) { + return R.fail("调拨数量不可超出源库存数量(当前库存:" + actualStock + ")"); + } + if (!inventoryList.isEmpty()) { + InventoryItem inv = inventoryList.get(0); + inventoryItemService.updateInventoryQuantity( + inv.getId(), inv.getQuantity().subtract(productTransferDto.getItemQuantity()), now); + } + SupplyRequest supplyRequest = new SupplyRequest(); BeanUtils.copyProperties(productTransferDto, supplyRequest); - supplyRequest.setTotalQuantity(productTransferDto.getTotalSourceQuantity()); if (productTransferDto.getId() != null) { - // 更新单据信息 supplyRequestService.updateById(supplyRequest); } else { - // 生成商品批量调拨单据 supplyRequest - // 单据分类:库存供应 .setCategoryEnum(SupplyCategory.STOCK_SUPPLY.getValue()) - // 单据类型:商品批量调拨 .setTypeEnum(SupplyType.PRODUCT_BATCH_TRANSFER.getValue()) - // 制单人 .setApplicantId(SecurityUtils.getLoginUser().getPractitionerId()) - // 申请时间 .setApplyTime(DateUtils.getNowDate()); supplyRequestService.save(supplyRequest); } - // 返回单据id return R.ok(supplyRequest.getId().toString(), null); } } - // 返回单据id return R.ok(idList, null); } @@ -332,33 +366,63 @@ public class ProductTransferAppServiceImpl implements IProductTransferAppService @Override public R addOrEditTransferReceipt(List productTransferDtoList) { - // 校验调拨数量:必须 > 0 且不超过源库存数量(从数据库查实时库存) Integer tenantId = SecurityUtils.getLoginUser().getTenantId(); - for (ProductTransferDto dto : productTransferDtoList) { - if (dto.getItemQuantity() == null || dto.getItemQuantity().compareTo(java.math.BigDecimal.ZERO) <= 0) { - return R.fail("调拨数量必须大于0"); - } - List inventoryList = inventoryItemService.selectInventoryByItemId( - dto.getItemId(), dto.getLotNumber(), dto.getSourceLocationId(), tenantId); - java.math.BigDecimal actualStock = inventoryList.stream() - .map(InventoryItem::getQuantity) - .reduce(java.math.BigDecimal.ZERO, java.math.BigDecimal::add); - if (dto.getItemQuantity().compareTo(actualStock) > 0) { - return R.fail("调拨数量不可超出源库存数量(当前库存:" + actualStock + ")"); - } - } + Date now = DateUtils.getNowDate(); List idList = new ArrayList<>(); // 单据号取得 List busNoList = productTransferDtoList.stream().map(ProductTransferDto::getBusNo).toList(); - // 请求数据取得 - List requestList = supplyRequestService.getSupplyByBusNo(busNoList.get(0)); - if (!requestList.isEmpty()) { - // 请求id取得 - List requestIdList = requestList.stream().map(SupplyRequest::getId).collect(Collectors.toList()); - // 单据信息删除 - supplyRequestService.removeByIds(requestIdList); + // 保存前:获取旧记录用于恢复预划扣库存 + List oldRequestList = supplyRequestService.getSupplyByBusNo(busNoList.get(0)); + Map oldDeductionMap = new HashMap<>(); + if (!oldRequestList.isEmpty()) { + for (SupplyRequest oldReq : oldRequestList) { + if (oldReq.getItemId() != null && oldReq.getLotNumber() != null && oldReq.getSourceLocationId() != null + && oldReq.getItemQuantity() != null) { + String key = oldReq.getItemId() + "_" + oldReq.getLotNumber() + "_" + oldReq.getSourceLocationId(); + oldDeductionMap.merge(key, oldReq.getItemQuantity(), BigDecimal::add); + } + } + // 恢复旧记录已预划扣的库存 + for (Map.Entry entry : oldDeductionMap.entrySet()) { + String[] parts = entry.getKey().split("_"); + Long itemId = Long.parseLong(parts[0]); + String lotNumber = parts[1]; + Long sourceLocationId = Long.parseLong(parts[2]); + BigDecimal restoreQty = entry.getValue(); + List invList = inventoryItemService.selectInventoryByItemId( + itemId, lotNumber, sourceLocationId, tenantId); + if (!invList.isEmpty()) { + inventoryItemService.updateInventoryQuantity( + invList.get(0).getId(), invList.get(0).getQuantity().add(restoreQty), now); + } + } + // 删除旧记录 + List oldIdList = oldRequestList.stream().map(SupplyRequest::getId).collect(Collectors.toList()); + supplyRequestService.removeByIds(oldIdList); + } + + // 校验 + 预划扣新记录 + Map newDeductionMap = new HashMap<>(); + for (ProductTransferDto dto : productTransferDtoList) { + if (dto.getItemQuantity() == null || dto.getItemQuantity().compareTo(BigDecimal.ZERO) <= 0) { + return R.fail("调拨数量必须大于0"); + } + List inventoryList = inventoryItemService.selectInventoryByItemId( + dto.getItemId(), dto.getLotNumber(), dto.getSourceLocationId(), tenantId); + BigDecimal actualStock = inventoryList.stream() + .map(InventoryItem::getQuantity) + .reduce(BigDecimal.ZERO, BigDecimal::add); + if (dto.getItemQuantity().compareTo(actualStock) > 0) { + return R.fail("调拨数量不可超出源库存数量(当前库存:" + actualStock + ")"); + } + // 预划扣:扣减源仓库库存 + if (!inventoryList.isEmpty()) { + InventoryItem inv = inventoryList.get(0); + inventoryItemService.updateInventoryQuantity( + inv.getId(), inv.getQuantity().subtract(dto.getItemQuantity()), now); + } } List supplyRequestList = new ArrayList<>(); @@ -405,6 +469,22 @@ public class ProductTransferAppServiceImpl implements IProductTransferAppService */ @Override public R deleteReceipt(List supplyRequestIds) { + // 删除前恢复预划扣的库存 + Integer tenantId = SecurityUtils.getLoginUser().getTenantId(); + Date now = DateUtils.getNowDate(); + for (Long reqId : supplyRequestIds) { + SupplyRequest sr = supplyRequestService.getById(reqId); + if (sr != null && sr.getItemId() != null && sr.getSourceLocationId() != null + && sr.getItemQuantity() != null && sr.getLotNumber() != null) { + List invList = inventoryItemService.selectInventoryByItemId( + sr.getItemId(), sr.getLotNumber(), sr.getSourceLocationId(), tenantId); + if (!invList.isEmpty()) { + InventoryItem inv = invList.get(0); + inventoryItemService.updateInventoryQuantity( + inv.getId(), inv.getQuantity().add(sr.getItemQuantity()), now); + } + } + } // 删除单据 boolean result = supplyRequestService.removeByIds(supplyRequestIds); return result ? R.ok(null, MessageUtils.createMessage(PromptMsgConstant.Common.M00004, null)) diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/inventorymanage/appservice/impl/ReceiptApprovalAppServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/inventorymanage/appservice/impl/ReceiptApprovalAppServiceImpl.java index 4ae7fdcfb..a8db9d76e 100755 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/inventorymanage/appservice/impl/ReceiptApprovalAppServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/inventorymanage/appservice/impl/ReceiptApprovalAppServiceImpl.java @@ -519,58 +519,8 @@ public class ReceiptApprovalAppServiceImpl implements IReceiptApprovalAppService // 暂时先取出全部的库存,循环查库存同一会有问题,后续优化 List inventoryItems = inventoryItemService.selectAllInventory(); for (SupplyItemDetailDto supplyItemDetailDto : supplyItemDetailList) { - // 根据项目id,产品批号,源仓库id 查询源仓库库存表信息 - // List inventoryItemSourceList = inventoryItemService.selectInventoryByItemId( - // supplyItemDetailDto.getItemId(), supplyItemDetailDto.getLotNumber(), - // supplyItemDetailDto.getSourceLocationId(), SecurityUtils.getLoginUser().getTenantId()); - List filteredInventoryItems = inventoryItems.stream() - .filter(item -> item.getItemId().equals(supplyItemDetailDto.getItemId()) - && item.getLotNumber().equals(supplyItemDetailDto.getLotNumber()) - && item.getLocationId().equals(supplyItemDetailDto.getSourceLocationId())) - .collect(Collectors.toList()); - - InventoryItem inventoryItemSource = new InventoryItem(); - if (!filteredInventoryItems.isEmpty()) { - inventoryItemSource = filteredInventoryItems.get(0); - // 最小数量(最小单位库存数量) - BigDecimal minQuantity = inventoryItemSource.getQuantity(); - - // // 计算调拨后库存数量,结果取小单位 - // // 供应申请的物品计量单位与包装单位相同 - // if (supplyItemDetailDto.getItemUnit().equals(supplyItemDetailDto.getUnitCode())) { - // if - // (minQuantity.compareTo(supplyItemDetailDto.getItemQuantity().multiply(supplyItemDetailDto.getPartPercent())) - // < 0) { - // // 库存数量不足 - // throw new ServiceException("操作失败,库存数量不足"); - // } else { - // // 源仓库库存-(调拨数量*拆零比) - // minQuantity = minQuantity.subtract( - // supplyItemDetailDto.getPartPercent().multiply(supplyItemDetailDto.getItemQuantity())); - // } - // } else if (supplyItemDetailDto.getItemUnit().equals(supplyItemDetailDto.getMinUnitCode())) { - // 直接扣减库存 - if (minQuantity.compareTo(supplyItemDetailDto.getItemQuantity()) < 0) { - // 库存数量不足 - throw new ServiceException("操作失败,库存数量不足"); - } else { - // 供应申请的物品计量单位与最小单位相同 - // 源仓库库存-调拨数量 - minQuantity = minQuantity.subtract(supplyItemDetailDto.getItemQuantity()); - } - // } - // 更新源仓库库存数量 - Boolean aBoolean - = inventoryItemService.updateInventoryQuantity(inventoryItemSource.getId(), minQuantity, now); - if (!aBoolean) { - throw new ServiceException("系统异常,请稍后重试"); - } - - // 添加到出库列表 - outList.add(supplyItemDetailDto); - } else { - return R.fail(MessageUtils.createMessage(PromptMsgConstant.Common.M00007, null)); - } + // 🔧 源仓库库存已在保存时预划扣,审批通过时不再重复扣减 + outList.add(supplyItemDetailDto); // 根据项目id,产品批号,目的仓库id 查询目的仓库库存表信息 List inventoryItemPurposeList = inventoryItemService.selectInventoryByItemId( diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/regdoctorstation/appservice/impl/AdviceManageAppServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/regdoctorstation/appservice/impl/AdviceManageAppServiceImpl.java index 7f8f3028e..202870833 100755 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/regdoctorstation/appservice/impl/AdviceManageAppServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/regdoctorstation/appservice/impl/AdviceManageAppServiceImpl.java @@ -36,6 +36,8 @@ import com.openhis.workflow.domain.ActivityDefinition; import com.openhis.workflow.service.IDeviceDispenseService; import com.openhis.workflow.service.IDeviceRequestService; import com.openhis.workflow.service.IServiceRequestService; +import com.openhis.document.domain.RequestForm; +import com.openhis.document.service.IRequestFormService; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -85,6 +87,9 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService { @Resource IDeviceDispenseService iDeviceDispenseService; + @Resource + IRequestFormService iRequestFormService; + /** * 查询住院患者信息 * @@ -266,6 +271,38 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService { log.info("开始处理删除操作,共 {} 条记录", deleteList.size()); + // 🔧 手术申请单级联作废:删除手术医嘱(categoryEnum=24)时同步作废关联的手术申请单 + Map> surgeryPrescriptionNoToRequestIds = new LinkedHashMap<>(); + for (RegAdviceSaveDto adviceDto : deleteList) { + if (adviceDto.getRequestId() == null) continue; + ServiceRequest existing = iServiceRequestService.getById(adviceDto.getRequestId()); + if (existing == null) continue; + if (existing.getCategoryEnum() != null + && existing.getCategoryEnum() == 24 + && existing.getPrescriptionNo() != null && !existing.getPrescriptionNo().isEmpty()) { + surgeryPrescriptionNoToRequestIds.computeIfAbsent(existing.getPrescriptionNo(), k -> new ArrayList<>()) + .add(adviceDto.getRequestId()); + } + } + for (Map.Entry> e : surgeryPrescriptionNoToRequestIds.entrySet()) { + String prescriptionNo = e.getKey(); + try { + List requestForms = iRequestFormService.list( + new LambdaQueryWrapper() + .eq(RequestForm::getPrescriptionNo, prescriptionNo) + .eq(RequestForm::getTypeCode, ActivityDefCategory.PROCEDURE.getCode()) + .and(w -> w.isNull(RequestForm::getDeleteFlag).or().eq(RequestForm::getDeleteFlag, "0"))); + for (RequestForm requestForm : requestForms) { + iRequestFormService.removeById(requestForm.getId()); + } + if (!requestForms.isEmpty()) { + log.info("级联作废手术申请单 prescriptionNo={}, 共{}条", prescriptionNo, requestForms.size()); + } + } catch (Exception ex) { + log.warn("级联作废手术申请单失败 prescriptionNo={}", prescriptionNo, ex); + } + } + for (RegAdviceSaveDto adviceDto : deleteList) { Integer adviceType = adviceDto.getAdviceType(); Long requestId = adviceDto.getRequestId(); diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/regdoctorstation/appservice/impl/RequestFormManageAppServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/regdoctorstation/appservice/impl/RequestFormManageAppServiceImpl.java index 2e09c86bc..d7047970a 100755 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/regdoctorstation/appservice/impl/RequestFormManageAppServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/regdoctorstation/appservice/impl/RequestFormManageAppServiceImpl.java @@ -81,16 +81,7 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer Long requestFormId = requestFormSaveDto.getRequestFormId(); boolean isEdit = requestFormId != null && requestFormId != 0L; - // 诊疗执行科室配置校验(必须在任何数据库操作之前) - List activityOrganizationConfig = - requestFormManageAppMapper.getActivityOrganizationConfig(typeCode); - if (activityOrganizationConfig.isEmpty()) { - throw new ServiceException("请先配置当前时间段的执行科室"); - } - - // 逐个校验activityList中的项目是否都配置了执行科室,并收集positionId供后续使用 - // 必须在任何数据库操作之前完成全部校验,避免部分保存后异常导致脏数据 - // 🔧 Bug #516: 优先使用前端传入的positionId(用户手动选择的发往科室),仅在未选择时使用配置的执行科室 + // 🔧 手术/检查申请单:优先使用前端传入的positionId(用户手动选择的发往科室),跳过执行科室配置校验 List activityList = requestFormSaveDto.getActivityList(); // 缓存校验结果,避免主循环中重复查询和可能出现的数据不一致 java.util.Map activityIdToPositionIdMap = new java.util.HashMap<>(); @@ -102,14 +93,15 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer activityIdToPositionIdMap.put(activitySaveDto.getAdviceDefinitionId(), frontendPositionId); continue; } - // 前端未传入时,使用配置的执行科室 + // 前端未传入时,查询配置的执行科室(暂不校验,仅用于兼容无前端传入的场景) + List activityOrganizationConfig = + requestFormManageAppMapper.getActivityOrganizationConfig(typeCode); Long configPositionId = activityOrganizationConfig.stream() .filter(dto -> activitySaveDto.getAdviceDefinitionId().equals(dto.getActivityDefinitionId())) .map(ActivityOrganizationConfigDto::getOrganizationId).findFirst().orElse(null); - if (configPositionId == null) { - throw new ServiceException(activitySaveDto.getAdviceDefinitionName() + "未配置当前时间段的执行科室"); + if (configPositionId != null) { + activityIdToPositionIdMap.put(activitySaveDto.getAdviceDefinitionId(), configPositionId); } - activityIdToPositionIdMap.put(activitySaveDto.getAdviceDefinitionId(), configPositionId); } } @@ -176,73 +168,77 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer ChargeItem chargeItem; log.info("保存申请单,typeCode={}, activityListSize={}, encounterId={}", typeCode, activityList != null ? activityList.size() : 0, encounterId); - for (ActivitySaveDto activitySaveDto : activityList) { - serviceRequest = new ServiceRequest(); - serviceRequest.setStatusEnum(RequestStatus.DRAFT.getValue()); - serviceRequest.setBusNo(assignSeqUtil.getSeqByDay(AssignSeqEnum.SERVICE_RES_NO.getPrefix(), 4)); - serviceRequest.setGenerateSourceEnum(GenerateSource.DOCTOR_PRESCRIPTION.getValue()); // 生成来源 - serviceRequest.setPrescriptionNo(prescriptionNo); - serviceRequest.setTherapyEnum(TherapyTimeType.TEMPORARY.getValue());// 治疗类型 - serviceRequest.setQuantity(activitySaveDto.getQuantity()); // 请求数量 - serviceRequest.setUnitCode(activitySaveDto.getUnitCode()); // 请求单位编码 - serviceRequest.setCategoryEnum(categoryEnum); // 请求类型 - serviceRequest.setActivityId(activitySaveDto.getAdviceDefinitionId());// 诊疗定义id - serviceRequest.setPatientId(patientId); // 患者 - serviceRequest.setRequesterId(practitionerId); // 开方医生 - serviceRequest.setEncounterId(encounterId); // 就诊id - serviceRequest.setAuthoredTime(curDate); // 请求签发时间 + // 🔧 手术申请单:跳过普通医嘱生成,只由 isProcedure 块生成手术医嘱,避免重复 + boolean isSurgeryRequest = ActivityDefCategory.PROCEDURE.getCode().equals(typeCode); + if (!isSurgeryRequest) { + for (ActivitySaveDto activitySaveDto : activityList) { + serviceRequest = new ServiceRequest(); + serviceRequest.setStatusEnum(RequestStatus.DRAFT.getValue()); + serviceRequest.setBusNo(assignSeqUtil.getSeqByDay(AssignSeqEnum.SERVICE_RES_NO.getPrefix(), 4)); + serviceRequest.setGenerateSourceEnum(GenerateSource.DOCTOR_PRESCRIPTION.getValue()); // 生成来源 + serviceRequest.setPrescriptionNo(prescriptionNo); + serviceRequest.setTherapyEnum(TherapyTimeType.TEMPORARY.getValue());// 治疗类型 + serviceRequest.setQuantity(activitySaveDto.getQuantity()); // 请求数量 + serviceRequest.setUnitCode(activitySaveDto.getUnitCode()); // 请求单位编码 + serviceRequest.setCategoryEnum(categoryEnum); // 请求类型 + serviceRequest.setActivityId(activitySaveDto.getAdviceDefinitionId());// 诊疗定义id + serviceRequest.setPatientId(patientId); // 患者 + serviceRequest.setRequesterId(practitionerId); // 开方医生 + serviceRequest.setEncounterId(encounterId); // 就诊id + serviceRequest.setAuthoredTime(curDate); // 请求签发时间 - Long positionId = activityIdToPositionIdMap.get(activitySaveDto.getAdviceDefinitionId()); - if (positionId == null) { - throw new ServiceException(activitySaveDto.getAdviceDefinitionName() + "未配置当前时间段的执行科室"); - } - serviceRequest.setOrgId(positionId); // 执行科室 + Long positionId = activityIdToPositionIdMap.get(activitySaveDto.getAdviceDefinitionId()); + if (positionId == null) { + throw new ServiceException(activitySaveDto.getAdviceDefinitionName() + "未配置当前时间段的执行科室"); + } + serviceRequest.setOrgId(positionId); // 执行科室 - serviceRequest.setYbClassEnum(activitySaveDto.getYbClassEnum());// 类别医保编码 - serviceRequest.setConditionId(activitySaveDto.getConditionId()); // 诊断id - serviceRequest.setEncounterDiagnosisId(activitySaveDto.getEncounterDiagnosisId()); // 就诊诊断id - iServiceRequestService.save(serviceRequest); + serviceRequest.setYbClassEnum(activitySaveDto.getYbClassEnum());// 类别医保编码 + serviceRequest.setConditionId(activitySaveDto.getConditionId()); // 诊断id + serviceRequest.setEncounterDiagnosisId(activitySaveDto.getEncounterDiagnosisId()); // 就诊诊断id + iServiceRequestService.save(serviceRequest); - chargeItem = new ChargeItem(); - chargeItem.setStatusEnum(ChargeItemStatus.DRAFT.getValue()); // 收费状态 - chargeItem.setBusNo(AssignSeqEnum.CHARGE_ITEM_NO.getPrefix().concat(serviceRequest.getBusNo())); - chargeItem.setGenerateSourceEnum(GenerateSource.DOCTOR_PRESCRIPTION.getValue()); // 生成来源 - chargeItem.setPatientId(patientId); // 患者 - chargeItem.setContextEnum(activitySaveDto.getAdviceType()); // 类型 - chargeItem.setEncounterId(encounterId); // 就诊id - chargeItem.setDefinitionId(activitySaveDto.getDefinitionId()); // 费用定价ID - chargeItem.setDefDetailId(activitySaveDto.getDefinitionDetailId()); // 定价子表主键 - chargeItem.setEntererId(practitionerId);// 开立人ID - chargeItem.setEnteredDate(curDate); // 开立时间 - chargeItem.setServiceTable(CommonConstants.TableName.WOR_SERVICE_REQUEST);// 医疗服务类型 - chargeItem.setServiceId(serviceRequest.getId()); // 医疗服务ID - chargeItem.setProductTable(CommonConstants.TableName.WOR_ACTIVITY_DEFINITION);// 产品所在表 - chargeItem.setProductId(activitySaveDto.getAdviceDefinitionId());// 收费项id - chargeItem.setAccountId(activitySaveDto.getAccountId());// 关联账户ID - chargeItem.setRequestingOrgId(orgId); // 开立科室 - chargeItem.setConditionId(activitySaveDto.getConditionId()); // 诊断id - chargeItem.setEncounterDiagnosisId(activitySaveDto.getEncounterDiagnosisId()); // 就诊诊断id - chargeItem.setQuantityValue(activitySaveDto.getQuantity()); // 数量 - chargeItem.setQuantityUnit(activitySaveDto.getUnitCode()); // 单位 - chargeItem.setUnitPrice(activitySaveDto.getUnitPrice()); // 单价 - chargeItem.setTotalPrice(activitySaveDto.getTotalPrice()); // 总价 - iChargeItemService.save(chargeItem); + chargeItem = new ChargeItem(); + chargeItem.setStatusEnum(ChargeItemStatus.DRAFT.getValue()); // 收费状态 + chargeItem.setBusNo(AssignSeqEnum.CHARGE_ITEM_NO.getPrefix().concat(serviceRequest.getBusNo())); + chargeItem.setGenerateSourceEnum(GenerateSource.DOCTOR_PRESCRIPTION.getValue()); // 生成来源 + chargeItem.setPatientId(patientId); // 患者 + chargeItem.setContextEnum(activitySaveDto.getAdviceType()); // 类型 + chargeItem.setEncounterId(encounterId); // 就诊id + chargeItem.setDefinitionId(activitySaveDto.getDefinitionId()); // 费用定价ID + chargeItem.setDefDetailId(activitySaveDto.getDefinitionDetailId()); // 定价子表主键 + chargeItem.setEntererId(practitionerId);// 开立人ID + chargeItem.setEnteredDate(curDate); // 开立时间 + chargeItem.setServiceTable(CommonConstants.TableName.WOR_SERVICE_REQUEST);// 医疗服务类型 + chargeItem.setServiceId(serviceRequest.getId()); // 医疗服务ID + chargeItem.setProductTable(CommonConstants.TableName.WOR_ACTIVITY_DEFINITION);// 产品所在表 + chargeItem.setProductId(activitySaveDto.getAdviceDefinitionId());// 收费项id + chargeItem.setAccountId(activitySaveDto.getAccountId());// 关联账户ID + chargeItem.setRequestingOrgId(orgId); // 开立科室 + chargeItem.setConditionId(activitySaveDto.getConditionId()); // 诊断id + chargeItem.setEncounterDiagnosisId(activitySaveDto.getEncounterDiagnosisId()); // 就诊诊断id + chargeItem.setQuantityValue(activitySaveDto.getQuantity()); // 数量 + chargeItem.setQuantityUnit(activitySaveDto.getUnitCode()); // 单位 + chargeItem.setUnitPrice(activitySaveDto.getUnitPrice()); // 单价 + chargeItem.setTotalPrice(activitySaveDto.getTotalPrice()); // 总价 + iChargeItemService.save(chargeItem); - // 处理诊疗套餐的子项信息 - ActivityDefinition activityDefinition = - iActivityDefinitionService.getById(activitySaveDto.getAdviceDefinitionId()); - String childrenJson = activityDefinition.getChildrenJson(); - if (childrenJson != null) { - // 诊疗子项参数类 - ActivityChildrenJsonParams activityChildrenJsonParams = new ActivityChildrenJsonParams(); - activityChildrenJsonParams.setTherapyEnum(TherapyTimeType.TEMPORARY.getValue()); // 治疗类型 - activityChildrenJsonParams.setPatientId(serviceRequest.getPatientId()); // 患者 - activityChildrenJsonParams.setEncounterId(serviceRequest.getEncounterId()); // 就诊id - activityChildrenJsonParams.setAccountId(chargeItem.getAccountId()); // 账户id - activityChildrenJsonParams.setChargeItemId(chargeItem.getId()); // 费用项id - activityChildrenJsonParams.setParentId(serviceRequest.getId());// 子项诊疗的父id - activityChildrenJsonParams.setEncounterDiagnosisId(serviceRequest.getEncounterDiagnosisId()); - adviceUtils.handleActivityChild(childrenJson, organizationId, activityChildrenJsonParams); + // 处理诊疗套餐的子项信息 + ActivityDefinition activityDefinition = + iActivityDefinitionService.getById(activitySaveDto.getAdviceDefinitionId()); + String childrenJson = activityDefinition.getChildrenJson(); + if (childrenJson != null) { + // 诊疗子项参数类 + ActivityChildrenJsonParams activityChildrenJsonParams = new ActivityChildrenJsonParams(); + activityChildrenJsonParams.setTherapyEnum(TherapyTimeType.TEMPORARY.getValue()); // 治疗类型 + activityChildrenJsonParams.setPatientId(serviceRequest.getPatientId()); // 患者 + activityChildrenJsonParams.setEncounterId(serviceRequest.getEncounterId()); // 就诊id + activityChildrenJsonParams.setAccountId(chargeItem.getAccountId()); // 账户id + activityChildrenJsonParams.setChargeItemId(chargeItem.getId()); // 费用项id + activityChildrenJsonParams.setParentId(serviceRequest.getId());// 子项诊疗的父id + activityChildrenJsonParams.setEncounterDiagnosisId(serviceRequest.getEncounterDiagnosisId()); + adviceUtils.handleActivityChild(childrenJson, organizationId, activityChildrenJsonParams); + } } } @@ -326,6 +322,13 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer } else if (surgeryName != null && !surgeryName.isEmpty()) { contentMap.put("surgeryName", surgeryName); } + // 🔧 手术申请单级联删除:contentJson 中记录 requestFormId 和 prescriptionNo,删除医嘱时可定位并作废申请单 + if (requestForm.getId() != null) { + contentMap.put("requestFormId", String.valueOf(requestForm.getId())); + } + if (prescriptionNo != null && !prescriptionNo.isEmpty()) { + contentMap.put("prescriptionNo", prescriptionNo); + } if (surgeryCode != null && !surgeryCode.isEmpty()) { contentMap.put("surgeryCode", surgeryCode); } @@ -368,9 +371,10 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer surgeryChargeItem.setServiceTable(CommonConstants.TableName.WOR_SERVICE_REQUEST); surgeryChargeItem.setServiceId(surgeryServiceRequest.getId()); surgeryChargeItem.setProductTable(CommonConstants.TableName.WOR_ACTIVITY_DEFINITION); - // 优先从 activityList 获取 productId + // 优先从 activityList 获取 productId 和 definitionId if (activityList != null && !activityList.isEmpty()) { surgeryChargeItem.setProductId(activityList.get(0).getAdviceDefinitionId()); + surgeryChargeItem.setDefinitionId(activityList.get(0).getDefinitionId()); surgeryChargeItem.setAccountId(activityList.get(0).getAccountId()); } surgeryChargeItem.setRequestingOrgId(orgId); @@ -409,6 +413,10 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer anesthesiaChargeItem.setServiceTable(CommonConstants.TableName.WOR_SERVICE_REQUEST); anesthesiaChargeItem.setServiceId(surgeryServiceRequest.getId()); anesthesiaChargeItem.setProductTable(CommonConstants.TableName.WOR_ACTIVITY_DEFINITION); + // 从 activityList 获取 definitionId + if (activityList != null && !activityList.isEmpty()) { + anesthesiaChargeItem.setDefinitionId(activityList.get(0).getDefinitionId()); + } anesthesiaChargeItem.setRequestingOrgId(orgId); anesthesiaChargeItem.setQuantityValue(BigDecimal.valueOf(1)); anesthesiaChargeItem.setQuantityUnit("次"); @@ -425,6 +433,18 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer log.error("生成手术医嘱过程中发生异常", e); throw e; } + // 将手术项目名称写入申请单name字段,确保医嘱删除后申请单仍保留正确名称 + if (activityList != null && !activityList.isEmpty()) { + String surgeryDisplayName = activityList.stream() + .map(ActivitySaveDto::getAdviceDefinitionName) + .filter(name -> name != null && !name.isEmpty()) + .distinct() + .collect(Collectors.joining("、")); + if (!surgeryDisplayName.isEmpty()) { + requestForm.setName(surgeryDisplayName); + iRequestFormService.updateById(requestForm); + } + } } else { log.info("不是手术申请单,跳过手术医嘱生成,typeCode={}", typeCode); } 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 4456911e9..c3067563c 100755 --- 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 @@ -117,7 +117,7 @@ ) - AND (t1.name ILIKE '%' || #{searchKey} || '%' OR t1.py_str ILIKE '%' || #{searchKey} || '%') + AND (t1.name ILIKE '%' || '${searchKey}' || '%' OR t1.py_str ILIKE '%' || '${searchKey}' || '%') AND t1.id IN @@ -181,7 +181,7 @@ WHERE t1.delete_flag = '0' AND t1.status_enum = #{statusEnum} - AND (t1.name ILIKE '%' || #{searchKey} || '%' OR t1.py_str ILIKE '%' || #{searchKey} || '%') + AND (t1.name ILIKE '%' || '${searchKey}' || '%' OR t1.py_str ILIKE '%' || '${searchKey}' || '%') AND t1.category_code = #{categoryCode} @@ -278,7 +278,7 @@ AND T1.category_code != '手术' AND T1.category_code != '24' - AND (t1.name ILIKE '%' || #{searchKey} || '%' OR t1.py_str ILIKE '%' || #{searchKey} || '%') + AND (t1.name ILIKE '%' || '${searchKey}' || '%' OR t1.py_str ILIKE '%' || '${searchKey}' || '%') AND t1.category_code = #{categoryCode} @@ -893,7 +893,6 @@ AND (t1.name ILIKE '%' || #{searchKey} || '%' OR t1.py_str ILIKE '%' || #{searchKey} || '%') ORDER BY t1.ID, t1.name ASC, t2.ID ASC - LIMIT #{page.size} OFFSET ${(page.current - 1) * page.size} diff --git a/openhis-ui-vue3/src/views/inpatientDoctor/home/components/applicationShow/surgeryApplication.vue b/openhis-ui-vue3/src/views/inpatientDoctor/home/components/applicationShow/surgeryApplication.vue index 2250bb54a..94002db1e 100755 --- a/openhis-ui-vue3/src/views/inpatientDoctor/home/components/applicationShow/surgeryApplication.vue +++ b/openhis-ui-vue3/src/views/inpatientDoctor/home/components/applicationShow/surgeryApplication.vue @@ -116,7 +116,7 @@ import {computed, getCurrentInstance, ref, watch} from 'vue'; import {Refresh} from '@element-plus/icons-vue'; import {patientInfo} from '../../store/patient.js'; import {getSurgery} from './api'; -import {getOrgList} from '@/views/doctorstation/components/api.js'; +import {getDepartmentList} from '@/api/public.js'; const { proxy } = getCurrentInstance(); @@ -182,25 +182,32 @@ const hasMatchedFields = computed(() => { /** 查询科室 */ const getLocationInfo = async () => { - const res = await getOrgList(); - orgOptions.value = res.data.records; + const res = await getDepartmentList(); + orgOptions.value = res.data || []; }; const recursionFun = (targetDepartment) => { + if (!targetDepartment || !orgOptions.value || orgOptions.value.length === 0) { + return ''; + } let name = ''; - for (let index = 0; index < orgOptions.value.length; index++) { - const obj = orgOptions.value[index]; - if (obj.id == targetDepartment) { - name = obj.name; - } - const subObjArray = obj['children']; - for (let index = 0; index < subObjArray.length; index++) { - const item = subObjArray[index]; - if (item.id == targetDepartment) { - name = item.name; + // 统一处理:扁平列表和树形结构都适用 + const findInList = (list) => { + for (const node of list) { + if (String(node.id) === String(targetDepartment)) { + name = node.name; + return true; + } + // 树形结构:递归查找 children + if (node.children && node.children.length > 0) { + if (findInList(node.children)) { + return true; + } } } - } + return false; + }; + findInList(orgOptions.value); return name; }; diff --git a/openhis-ui-vue3/src/views/inpatientDoctor/home/components/order/applicationForm/surgery.vue b/openhis-ui-vue3/src/views/inpatientDoctor/home/components/order/applicationForm/surgery.vue index 0e50c0431..12c34605b 100755 --- a/openhis-ui-vue3/src/views/inpatientDoctor/home/components/order/applicationForm/surgery.vue +++ b/openhis-ui-vue3/src/views/inpatientDoctor/home/components/order/applicationForm/surgery.vue @@ -232,64 +232,21 @@ onMounted(() => { * type(1:watch监听类型 2:点击保存类型) * selectProjectIds(选中项目的id数组) * */ -const projectWithDepartment = (selectProjectIds, type) => { - //1.获取选中的项目 2.判断项目的执行科室是否相同 3.判断执行科室是否配置 4.将项目的执行科室复值到执行科室下拉选位置 - let isRelease = true; - // 选中项目的数组 - const arr = []; - // 根据选中的项目id查找对应的项目 - selectProjectIds.forEach((element) => { - const searchData = applicationList.value.find((item) => { - return element == item.adviceDefinitionId; - }); - arr.push(searchData); - }); - // 清空科室 - form.targetDepartment = ''; - if (arr.length > 0) { - const obj = arr[0]; - // 判断科室是否相同 - const isCompare = arr.every((item) => { - return item.orgId == obj.orgId; - }); - if (!isCompare) { - ElMessage({ - type: 'error', - message: '执行科室不同', - }); - isRelease = false; - } - // 选中项目中的执行科室id与全部科室数据做匹配 - const findItem = findTreeItem(orgOptions.value, obj.orgId); - - if (!findItem) { - isRelease = false; - ElMessage({ - type: 'error', - message: '未找到项目执行的科室', - }); - } - if (type == 1) { - if (isRelease) { - form.targetDepartment = findItem.id; - } - } +const projectWithDepartment = (selectProjectIds) => { + if (!selectProjectIds || selectProjectIds.length === 0) { + form.targetDepartment = ''; } - return isRelease; }; // 监听选择项目变化 -watch( - () => transferValue.value, - (newValue) => { - projectWithDepartment(newValue, 1); - } -); +watch(() => transferValue.value, (newValue) => { + projectWithDepartment(newValue); +}); const submit = () => { if (transferValue.value.length == 0) { - return proxy.$message.error('请选择申请单'); + return proxy.$message.error('请选择手术项目'); } - if (!projectWithDepartment(transferValue.value, 2)) { - return; + if (!form.targetDepartment) { + return proxy.$message.error('请选择发往科室'); } let applicationListAllFilter = applicationListAll.value.filter((item) => { return transferValue.value.includes(item.adviceDefinitionId); @@ -302,7 +259,7 @@ const submit = () => { unitCode: item.unitCode, unitPrice: item.price, totalPrice: item.price, - positionId: item.positionId, + positionId: form.targetDepartment || item.positionId, // 用户手动选择的发往科室优先于项目默认执行科室 definitionId: item.chargeItemDefinitionId, accountId: patientInfo.value.accountId, }; diff --git a/openhis-ui-vue3/src/views/inpatientDoctor/home/components/order/index.vue b/openhis-ui-vue3/src/views/inpatientDoctor/home/components/order/index.vue index 4ef02c14f..1d4b059f5 100755 --- a/openhis-ui-vue3/src/views/inpatientDoctor/home/components/order/index.vue +++ b/openhis-ui-vue3/src/views/inpatientDoctor/home/components/order/index.vue @@ -1195,7 +1195,7 @@ function handleSave() { // 此处签发处方和单行保存处方传参相同,后台已经将传参存为JSON字符串,此处直接转换为JSON即可 loading.value = true; let list = saveList.map((item) => { - const parsedContent = JSON.parse(item.contentJson); + const parsedContent = item.contentJson ? JSON.parse(item.contentJson) : {}; return { ...parsedContent, adviceType: item.adviceType, diff --git a/openhis-ui-vue3/src/views/inpatientNurse/InpatientBilling/components/FeeDialog.vue b/openhis-ui-vue3/src/views/inpatientNurse/InpatientBilling/components/FeeDialog.vue index cc86b9fe6..e084a5b89 100755 --- a/openhis-ui-vue3/src/views/inpatientNurse/InpatientBilling/components/FeeDialog.vue +++ b/openhis-ui-vue3/src/views/inpatientNurse/InpatientBilling/components/FeeDialog.vue @@ -373,7 +373,8 @@ const filterKeywords = ref({}); const queryParams = ref({ pageSize: 100, pageNum: 1, - adviceTypes: '2,3', + // 默认加载全部类型(药品1+耗材2+诊疗3) + adviceTypes: [1, 2, 3], }); /** * 医嘱提交数据模型 @@ -555,12 +556,26 @@ function loadDepartmentOptions() { function getAdviceBaseInfos() { adviceLoading.value = true; queryParams.value.searchKey = searchText.value; - queryParams.value.adviceTypes = adviceType.value; + // 字典值(3=诊疗,4=耗材)映射为后端adviceType(2=耗材,3=诊疗) + if (adviceType.value === 4) { + queryParams.value.adviceTypes = [2]; + } else if (adviceType.value === 3) { + queryParams.value.adviceTypes = [3]; + } else { + queryParams.value.adviceTypes = [1, 2, 3]; + } queryParams.value.organizationId = orgId.value; queryParams.value.pricingFlag = 1; // 划价标记 getAdviceBaseInfo(queryParams.value) .then((res) => { - AdviceBaseInfoList.value = res.data?.records || []; + const list = res.data?.records || []; + // 药品(1)和耗材(2)必须有库存才能展示,诊疗(3)无库存概念不过滤 + AdviceBaseInfoList.value = list.filter(item => { + if (item.adviceType === 1 || item.adviceType === 2) { + return item.inventoryList && item.inventoryList.length > 0; + } + return true; + }); }) .finally(() => { adviceLoading.value = false; diff --git a/openhis-ui-vue3/src/views/medicationmanagement/transferManagent/transferManagent/index.vue b/openhis-ui-vue3/src/views/medicationmanagement/transferManagent/transferManagent/index.vue index bbed6fb1b..d134941df 100755 --- a/openhis-ui-vue3/src/views/medicationmanagement/transferManagent/transferManagent/index.vue +++ b/openhis-ui-vue3/src/views/medicationmanagement/transferManagent/transferManagent/index.vue @@ -386,7 +386,7 @@ :disabled="viewStatus == 'view'" v-model="scope.row.itemQuantityDisplay" placeholder="" - @change="(value) => handleItemQuantityChange(scope.row, value)" + @input="(value) => handleItemQuantityChange(scope.row, value)" :class="{ 'error-border': scope.row.error }" /> @@ -971,6 +971,10 @@ function handleItemQuantityChange(row, value) { quantityTemp = value; } row.totalPrice = ((row.price * quantityTemp) / row.partPercent).toFixed(2); + // 数量变更后重置保存标记,允许重新提交 + row.isSave = false; + // 同步更新底部合计金额 + handleTotalAmount(); } function handelApply() { @@ -1262,128 +1266,108 @@ function editBatchTransfer(index) { } function handleSave(row, index) { - rowList.value = []; - if (route.query.supplyBusNo) { - // 编辑 - forms.purchaseinventoryList.map((row, index) => { - if (row) { - proxy.$refs['receiptHeaderRef'].validate((valid) => { - if (valid) { - proxy.$refs['formRef'].validate((valid) => { - if (valid) { - if (row.unitCode == row.unitList.minUnitCode) { - row.itemQuantity = forms.purchaseinventoryList[index].olditemQuantity - ? forms.purchaseinventoryList[index].olditemQuantity - : forms.purchaseinventoryList[index].itemQuantity; - } else { - row.itemQuantity = forms.purchaseinventoryList[index].itemMaxQuantity - ? forms.purchaseinventoryList[index].itemMaxQuantity - : forms.purchaseinventoryList[index].itemQuantity; - } - // let rows = JSON.parse(JSON.stringify(row)) - // delete rows.itemMaxQuantity - - if (row.unitCode == row.unitCode_dictText) { - if (row.unitCode_dictText == row.unitList.minUnitCode_dictText) { - row.unitCode = row.unitList.minUnitCode; - } else { - row.unitCode = row.unitList.unitCode; - row.unitCode_dictText = row.unitList.unitCode_dictText; - } - } - if (row.unitCode == row.unitList.unitCode) { - row.unitCode_dictText = row.unitList.unitCode_dictText; - } else if (row.unitCode == row.unitList.minUnitCode) { - row.unitCode_dictText = row.unitList.minUnitCode_dictText; - } - if (!forms.purchaseinventoryList[index].price || forms.purchaseinventoryList[index].price <= 0) { - proxy.$message.warning('调拨单价不能为空或为0,请检查!'); - return; - } - forms.purchaseinventoryList[index].totalPrice = - forms.purchaseinventoryList[index].price * forms.purchaseinventoryList[index].itemQuantity; - rowList.value.push(JSON.parse(JSON.stringify(row))); - if ( - rowList._rawValue && - rowList._rawValue.length == forms.purchaseinventoryList.length - ) { - addTransferProducts(rowList._rawValue); - } - } - }); - } - }); - } - }); - } else { - //新增 - form.purchaseinventoryList.map((row, index) => { - if (row) { - proxy.$refs['receiptHeaderRef'].validate((valid) => { - if (valid) { - proxy.$refs['formRef'].validate((valid) => { - if (valid) { - let rows = JSON.parse(JSON.stringify(row)); - delete rows.itemMaxQuantity; - if (rows.unitCode == rows.unitList.minUnitCode) { - rows.itemQuantity = form.purchaseinventoryList[index].olditemQuantity - ? form.purchaseinventoryList[index].olditemQuantity - : form.purchaseinventoryList[index].itemQuantity; - } else { - rows.itemQuantity = form.purchaseinventoryList[index].itemMaxQuantity - ? form.purchaseinventoryList[index].itemMaxQuantity - : form.purchaseinventoryList[index].itemQuantity; - } - if (rows.unitCode == rows.unitCode_dictText) { - if (rows.unitCode_dictText == rows.unitList.minUnitCode_dictText) { - rows.unitCode = rows.unitList.minUnitCode; - } else { - rows.unitCode = rows.unitList.unitCode; - rows.unitCode_dictText = rows.unitList.unitCode_dictText; - } - } - if (rows.unitCode == rows.unitList.unitCode) { - rows.unitCode_dictText = rows.unitList.unitCode_dictText; - } else if (rows.unitCode == rows.unitList.minUnitCode) { - rows.unitCode_dictText = rows.unitList.minUnitCode_dictText; - } - if (!form.purchaseinventoryList[index].price || form.purchaseinventoryList[index].price <= 0) { - proxy.$message.warning('调拨单价不能为空或为0,请检查!'); - return; - } - form.purchaseinventoryList[index].totalPrice = - form.purchaseinventoryList[index].price * form.purchaseinventoryList[index].itemQuantity; - rowList.value.push(JSON.parse(JSON.stringify(rows))); - if ( - rowList._rawValue && - rowList._rawValue.length == form.purchaseinventoryList.length - ) { - addTransferProducts(rowList._rawValue); - } - } - }); - } - }); - } - }); + // 过滤出未保存的行,已保存的行不重复提交 + const listToCheck = route.query.supplyBusNo + ? forms.purchaseinventoryList + : form.purchaseinventoryList; + const unsavedList = listToCheck.filter(item => !item.isSave); + if (unsavedList.length === 0) { + proxy.$modal.msgWarning('所有行均已保存,无需重复提交'); + return; } + + // 先校验表头 + proxy.$refs['receiptHeaderRef'].validate((headerValid) => { + if (!headerValid) return; + + // 逐行校验(避免异步回调导致重复提交) + const rowsToSave = []; + for (let i = 0; i < form.purchaseinventoryList.length; i++) { + const r = form.purchaseinventoryList[i]; + if (!r) continue; + + // 跳过已保存的行,避免重复提交导致预扣减库存叠加 + if (r.isSave) continue; + + // 校验当前行的必填字段 + let rowValid = true; + for (const prop of ['name', 'unitCode']) { + const formRef = proxy.$refs['formRef']; + if (formRef && formRef.validateField) { + formRef.validateField(`purchaseinventoryList.${i}.${prop}`, (valid) => { + if (valid) rowValid = false; + }); + } + } + if (!rowValid) { + proxy.$modal.msgWarning('第' + (i + 1) + '行数据不完整,请检查'); + return; + } + + // 单价校验 + if (!r.price || r.price <= 0) { + proxy.$modal.msgWarning('第' + (i + 1) + '行调拨单价不能为空或为0'); + return; + } + + // 单位处理 + const rowData = route.query.supplyBusNo + ? JSON.parse(JSON.stringify(forms.purchaseinventoryList[i])) + : JSON.parse(JSON.stringify(r)); + delete rowData.itemMaxQuantity; + + if (rowData.unitCode == rowData.unitList?.minUnitCode) { + rowData.itemQuantity = r.olditemQuantity || r.itemQuantity; + } else { + rowData.itemQuantity = r.itemMaxQuantity || r.itemQuantity; + } + + if (rowData.unitCode == rowData.unitCode_dictText) { + if (rowData.unitCode_dictText == rowData.unitList?.minUnitCode_dictText) { + rowData.unitCode = rowData.unitList.minUnitCode; + } else { + rowData.unitCode = rowData.unitList.unitCode; + rowData.unitCode_dictText = rowData.unitList.unitCode_dictText; + } + } + if (rowData.unitCode == rowData.unitList?.unitCode) { + rowData.unitCode_dictText = rowData.unitList.unitCode_dictText; + } else if (rowData.unitCode == rowData.unitList?.minUnitCode) { + rowData.unitCode_dictText = rowData.unitList.minUnitCode_dictText; + } + + // 计算总价 + r.totalPrice = r.price * rowData.itemQuantity; + rowsToSave.push(rowData); + } + + // 所有行校验通过,一次性提交 + if (rowsToSave.length > 0) { + addTransferProducts(rowsToSave); + } + }); } function addTransferProducts(rowList) { addTransferProduct(JSON.parse(JSON.stringify(rowList))).then((res) => { - // 当前行没有id视为首次新增 - // if (!row.id) { - // data.isAdding = false; // 允许新增下一行 - // } if (res.data) { proxy.$message.success('保存成功!'); + let newIdIndex = 0; form.purchaseinventoryList.map((row, index) => { - form.purchaseinventoryList[index].id = res.data[index]; + // 只有未保存的行才会拿到新 id(和提交顺序一致) + if (!row.isSave && res.data[newIdIndex]) { + form.purchaseinventoryList[index].id = res.data[newIdIndex]; + newIdIndex++; + } form.purchaseinventoryList[index].isSave = true; }); if (route.query.supplyBusNo) { // 编辑 + let newIdIdx = 0; forms.purchaseinventoryList.map((row, index) => { - forms.purchaseinventoryList[index].id = res.data[index]; + if (!row.isSave && res.data[newIdIdx]) { + forms.purchaseinventoryList[index].id = res.data[newIdIdx]; + newIdIdx++; + } forms.purchaseinventoryList[index].isSave = true; }); }