bug 514 537 538 540 543

This commit is contained in:
Ranyunqiao
2026-05-18 17:44:15 +08:00
parent 4972ca64da
commit 0e974129eb
12 changed files with 512 additions and 396 deletions

View File

@@ -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<String, List<Long>> labApplyNoToRequestIds = new LinkedHashMap<>();
Map<String, List<Long>> surgeryPrescriptionNoToRequestIds = new LinkedHashMap<>();
// 收集待删除的 ServiceRequest先查询再删除避免级联逻辑因记录已删除而失效
Map<Long, ServiceRequest> 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<Long> labCascadeSkippedRequestIds = new HashSet<>();
// 执行检验申请单级联作废
Set<Long> cascadeSkippedRequestIds = new HashSet<>();
for (Map.Entry<String, List<Long>> 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<String, List<Long>> e : surgeryPrescriptionNoToRequestIds.entrySet()) {
String prescriptionNo = e.getKey();
try {
List<RequestForm> requestForms = iRequestFormService.list(
new LambdaQueryWrapper<RequestForm>()
.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<Surgery>()
.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<SurgeryItemDto> records = doctorStationAdviceAppMapper.getSurgeryPage(
// 使用 MyBatis Plus 分页查询
IPage<SurgeryItemDto> result = doctorStationAdviceAppMapper.getSurgeryPage(
new Page<>(pageNo, pageSize),
PublicationStatus.ACTIVE.getValue(),
organizationId,
searchKey);
// 手动构造 Page 对象total 设为 records.size()(前端 el-transfer 不需要精确的 total 总数)
IPage<SurgeryItemDto> 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);
}

View File

@@ -195,7 +195,7 @@ public interface DoctorStationAdviceAppMapper {
* @param searchKey 模糊查询关键字(可选)
* @return 手术项目分页数据
*/
List<SurgeryItemDto> getSurgeryPage(@Param("page") Page<SurgeryItemDto> page,
IPage<SurgeryItemDto> getSurgeryPage(@Param("page") Page<SurgeryItemDto> page,
@Param("statusEnum") Integer statusEnum,
@Param("organizationId") Long organizationId,
@Param("searchKey") String searchKey);

View File

@@ -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<ProductTransferDto> 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<InventoryItem> 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<String> idList = new ArrayList<>();
if (flag) {
// 批量保存按钮
// 单据号取得
List<String> busNoList = productTransferDtoList.stream().map(ProductTransferDto::getBusNo).toList();
// 请求id取得
List<SupplyRequest> requestList = supplyRequestService.getSupplyByBusNo(busNoList.get(0));
if (!requestList.isEmpty()) {
List<Long> requestIdList = requestList.stream().map(SupplyRequest::getId).collect(Collectors.toList());
// 单据信息删除
supplyRequestService.removeByIds(requestIdList);
// 保存前:获取旧记录用于恢复预划扣库存
List<SupplyRequest> 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<InventoryItem> 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<Long> 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<InventoryItem> 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<SupplyRequest> 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<SupplyRequest> supplyRequestIdList = supplyRequestService.getSupplyByBusNo(busNoList.get(0));
// 返回请求id列表
List<Long> 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<InventoryItem> 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<InventoryItem> 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<ProductTransferDto> 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<InventoryItem> 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<String> idList = new ArrayList<>();
// 单据号取得
List<String> busNoList = productTransferDtoList.stream().map(ProductTransferDto::getBusNo).toList();
// 请求数据取得
List<SupplyRequest> requestList = supplyRequestService.getSupplyByBusNo(busNoList.get(0));
if (!requestList.isEmpty()) {
// 请求id取得
List<Long> requestIdList = requestList.stream().map(SupplyRequest::getId).collect(Collectors.toList());
// 单据信息删除
supplyRequestService.removeByIds(requestIdList);
// 保存前:获取旧记录用于恢复预划扣库存
List<SupplyRequest> oldRequestList = supplyRequestService.getSupplyByBusNo(busNoList.get(0));
Map<String, BigDecimal> 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<String, BigDecimal> 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<InventoryItem> invList = inventoryItemService.selectInventoryByItemId(
itemId, lotNumber, sourceLocationId, tenantId);
if (!invList.isEmpty()) {
inventoryItemService.updateInventoryQuantity(
invList.get(0).getId(), invList.get(0).getQuantity().add(restoreQty), now);
}
}
// 删除旧记录
List<Long> oldIdList = oldRequestList.stream().map(SupplyRequest::getId).collect(Collectors.toList());
supplyRequestService.removeByIds(oldIdList);
}
// 校验 + 预划扣新记录
Map<String, BigDecimal> newDeductionMap = new HashMap<>();
for (ProductTransferDto dto : productTransferDtoList) {
if (dto.getItemQuantity() == null || dto.getItemQuantity().compareTo(BigDecimal.ZERO) <= 0) {
return R.fail("调拨数量必须大于0");
}
List<InventoryItem> 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<SupplyRequest> supplyRequestList = new ArrayList<>();
@@ -405,6 +469,22 @@ public class ProductTransferAppServiceImpl implements IProductTransferAppService
*/
@Override
public R<?> deleteReceipt(List<Long> 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<InventoryItem> 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))

View File

@@ -519,58 +519,8 @@ public class ReceiptApprovalAppServiceImpl implements IReceiptApprovalAppService
// 暂时先取出全部的库存,循环查库存同一会有问题,后续优化
List<InventoryItem> inventoryItems = inventoryItemService.selectAllInventory();
for (SupplyItemDetailDto supplyItemDetailDto : supplyItemDetailList) {
// 根据项目id,产品批号源仓库id 查询源仓库库存表信息
// List<InventoryItem> inventoryItemSourceList = inventoryItemService.selectInventoryByItemId(
// supplyItemDetailDto.getItemId(), supplyItemDetailDto.getLotNumber(),
// supplyItemDetailDto.getSourceLocationId(), SecurityUtils.getLoginUser().getTenantId());
List<InventoryItem> 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<InventoryItem> inventoryItemPurposeList = inventoryItemService.selectInventoryByItemId(

View File

@@ -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<String, List<Long>> 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<String, List<Long>> e : surgeryPrescriptionNoToRequestIds.entrySet()) {
String prescriptionNo = e.getKey();
try {
List<RequestForm> requestForms = iRequestFormService.list(
new LambdaQueryWrapper<RequestForm>()
.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();

View File

@@ -81,16 +81,7 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer
Long requestFormId = requestFormSaveDto.getRequestFormId();
boolean isEdit = requestFormId != null && requestFormId != 0L;
// 诊疗执行科室配置校验(必须在任何数据库操作之前)
List<ActivityOrganizationConfigDto> activityOrganizationConfig =
requestFormManageAppMapper.getActivityOrganizationConfig(typeCode);
if (activityOrganizationConfig.isEmpty()) {
throw new ServiceException("请先配置当前时间段的执行科室");
}
// 逐个校验activityList中的项目是否都配置了执行科室并收集positionId供后续使用
// 必须在任何数据库操作之前完成全部校验,避免部分保存后异常导致脏数据
// 🔧 Bug #516: 优先使用前端传入的positionId用户手动选择的发往科室仅在未选择时使用配置的执行科室
// 🔧 手术/检查申请单优先使用前端传入的positionId用户手动选择的发往科室跳过执行科室配置校验
List<ActivitySaveDto> activityList = requestFormSaveDto.getActivityList();
// 缓存校验结果,避免主循环中重复查询和可能出现的数据不一致
java.util.Map<Long, Long> activityIdToPositionIdMap = new java.util.HashMap<>();
@@ -102,14 +93,15 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer
activityIdToPositionIdMap.put(activitySaveDto.getAdviceDefinitionId(), frontendPositionId);
continue;
}
// 前端未传入时,使用配置的执行科室
// 前端未传入时,查询配置的执行科室(暂不校验,仅用于兼容无前端传入的场景)
List<ActivityOrganizationConfigDto> 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);
}

View File

@@ -117,7 +117,7 @@
)
</if>
<if test="searchKey != null and searchKey != ''">
AND (t1.name ILIKE '%' || #{searchKey} || '%' OR t1.py_str ILIKE '%' || #{searchKey} || '%')
AND (t1.name ILIKE '%' || '${searchKey}' || '%' OR t1.py_str ILIKE '%' || '${searchKey}' || '%')
</if>
<if test="adviceDefinitionIdParamList != null and !adviceDefinitionIdParamList.isEmpty()">
AND t1.id IN
@@ -181,7 +181,7 @@
WHERE t1.delete_flag = '0'
AND t1.status_enum = #{statusEnum}
<if test="searchKey != null and searchKey != ''">
AND (t1.name ILIKE '%' || #{searchKey} || '%' OR t1.py_str ILIKE '%' || #{searchKey} || '%')
AND (t1.name ILIKE '%' || '${searchKey}' || '%' OR t1.py_str ILIKE '%' || '${searchKey}' || '%')
</if>
<if test="categoryCode != null and categoryCode != ''">
AND t1.category_code = #{categoryCode}
@@ -278,7 +278,7 @@
AND T1.category_code != '手术' AND T1.category_code != '24'
</if>
<if test="searchKey != null and searchKey != ''">
AND (t1.name ILIKE '%' || #{searchKey} || '%' OR t1.py_str ILIKE '%' || #{searchKey} || '%')
AND (t1.name ILIKE '%' || '${searchKey}' || '%' OR t1.py_str ILIKE '%' || '${searchKey}' || '%')
</if>
<if test="categoryCode != null and categoryCode != ''">
AND t1.category_code = #{categoryCode}
@@ -893,7 +893,6 @@
AND (t1.name ILIKE '%' || #{searchKey} || '%' OR t1.py_str ILIKE '%' || #{searchKey} || '%')
</if>
ORDER BY t1.ID, t1.name ASC, t2.ID ASC
LIMIT #{page.size} OFFSET ${(page.current - 1) * page.size}
</select>
<!-- 检查项目专用分页查询:仅查检查(23) + 定价,无库存/草稿库存/取药科室等无关逻辑 -->

View File

@@ -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;
};

View File

@@ -232,64 +232,21 @@ onMounted(() => {
* type(1watch监听类型 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,
};

View File

@@ -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,

View File

@@ -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;

View File

@@ -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 }"
/>
</div>
@@ -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;
});
}