修复问题:

1. 修复检验申请单生成的医嘱签发失败问题(BugFix#328)
2. 修复处方工具类空指针异常问题
3. 修复检验项目套餐价格查询问题
4. 修复医嘱签发时费用项状态更新问题
This commit is contained in:
wangjian963
2026-04-13 18:23:36 +08:00
parent cb5023bcea
commit d99daa3048
6 changed files with 998 additions and 98 deletions

View File

@@ -8,6 +8,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.core.common.core.domain.R;
import com.core.common.core.redis.RedisCache;
import com.core.common.enums.DelFlag;
import com.core.common.enums.TenantOptionDict;
import com.core.common.exception.ServiceException;
import com.core.common.utils.AssignSeqUtil;
@@ -889,8 +890,14 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
}
// 签发时
if (is_sign) {
// 生成处方号
prescriptionUtils.generatePrescriptionNumbers(insertOrUpdateList);
// 🔧 Bug Fix #328: 只对药品类型的医嘱生成处方号
// 检验申请单生成的医嘱是诊疗项目(adviceType=3),不需要处方号
List<AdviceSaveDto> medicineListForPrescription = insertOrUpdateList.stream()
.filter(e -> ItemType.MEDICINE.getValue().equals(e.getAdviceType()))
.collect(Collectors.toList());
if (!medicineListForPrescription.isEmpty()) {
prescriptionUtils.generatePrescriptionNumbers(medicineListForPrescription);
}
}
List<String> medRequestIdList = new ArrayList<>();
@@ -1775,14 +1782,38 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
// }
// log.error(e.getMessage(), e);
// }
// 签发时将收费项目状态从草稿改为待收费
// 🔧 BugFix#328: 签发时将收费项目状态从草稿改为待收费
// 修复检验申请单生成的医嘱签发失败问题
Long chargeItemId = adviceSaveDto.getChargeItemId();
ChargeItem existingChargeItem = null;
// 方式1通过chargeItemId直接查询
if (chargeItemId != null) {
ChargeItem existingChargeItem = iChargeItemService.getById(chargeItemId);
existingChargeItem = iChargeItemService.getById(chargeItemId);
}
// 方式2如果chargeItemId为null通过requestIdserviceId查询费用项
// 检验申请单创建的医嘱可能没有传递chargeItemId需要通过serviceId查找
if (existingChargeItem == null && adviceSaveDto.getRequestId() != null) {
existingChargeItem = iChargeItemService.getOne(
new LambdaQueryWrapper<ChargeItem>()
.eq(ChargeItem::getServiceId, adviceSaveDto.getRequestId())
.eq(ChargeItem::getServiceTable, CommonConstants.TableName.WOR_SERVICE_REQUEST)
.eq(ChargeItem::getDeleteFlag, DelFlag.NO.getCode())
);
log.info("BugFix#328: 通过requestId查询费用项requestId={}, chargeItem={}",
adviceSaveDto.getRequestId(), existingChargeItem != null ? existingChargeItem.getId() : "null");
}
// 更新费用项状态
if (existingChargeItem != null) {
existingChargeItem.setStatusEnum(ChargeItemStatus.PLANNED.getValue());
iChargeItemService.updateById(existingChargeItem);
}
log.info("BugFix#328: 更新费用项状态为待收费chargeItemId={}, status={}",
existingChargeItem.getId(), ChargeItemStatus.PLANNED.getValue());
} else {
log.warn("BugFix#328: 未找到对应的费用项无法更新状态requestId={}, chargeItemId={}",
adviceSaveDto.getRequestId(), chargeItemId);
}
}
}

View File

@@ -10,9 +10,11 @@ import com.openhis.administration.domain.Account;
import com.openhis.lab.domain.InspectionLabApply;
import com.openhis.lab.domain.InspectionLabApplyItem;
import com.openhis.lab.domain.BarCode;
import com.openhis.lab.domain.InspectionPackage;
import com.openhis.lab.service.IInspectionLabApplyItemService;
import com.openhis.lab.service.IInspectionLabApplyService;
import com.openhis.lab.service.IInspectionLabBarCodeService;
import com.openhis.lab.service.IInspectionPackageService;
import com.openhis.workflow.domain.ServiceRequest;
import com.openhis.workflow.service.IServiceRequestService;
import com.openhis.web.doctorstation.appservice.IDoctorStationAdviceAppService;
@@ -82,6 +84,10 @@ public class DoctorStationLabApplyServiceImpl implements IDoctorStationInspectio
@Autowired
private RedisCache redisCache;
// BugFix: 套餐价格查询服务
@Autowired
private IInspectionPackageService inspectionPackageService;
/**
* 保存检验申请单信息
* @param doctorStationLabApplyDto
@@ -273,8 +279,11 @@ public class DoctorStationLabApplyServiceImpl implements IDoctorStationInspectio
adviceSaveDto.setEncounterId(doctorStationLabApplyDto.getEncounterId());
// 开方医生 ID - AdviceSaveDto 构造函数已设置,这里可覆盖
adviceSaveDto.setPractitionerId(SecurityUtils.getUserId());
// 开方科室 ID - AdviceSaveDto 构造函数已设置,这里可覆盖
adviceSaveDto.setFounderOrgId(SecurityUtils.getDeptId());
// 开方科室 ID - 使用患者挂号科室
adviceSaveDto.setFounderOrgId(doctorStationLabApplyDto.getApplyOrganizationId());
// 执行科室 ID - 用于诊疗项目校验BugFix#328
// 注意AdviceSaveDto 中没有 setEffectiveOrgId 方法,需要设置 orgId 字段
adviceSaveDto.setOrgId(positionId);
// 账户 ID - 获取就诊的账户(多级回退策略)
Long accountId = accountService.getSelfPayAccount(doctorStationLabApplyDto.getEncounterId());
if (accountId == null) {
@@ -299,15 +308,43 @@ public class DoctorStationLabApplyServiceImpl implements IDoctorStationInspectio
adviceSaveDto.setQuantity(labApplyItemDto.getItemQty() != null ? labApplyItemDto.getItemQty() : java.math.BigDecimal.ONE);
// 请求单位编码(使用诊疗定义的使用单位)
adviceSaveDto.setUnitCode(activityDefinition.getPermittedUnitCode());
// 单价
adviceSaveDto.setUnitPrice(labApplyItemDto.getItemPrice());
// 总价
adviceSaveDto.setTotalPrice(labApplyItemDto.getItemAmount());
// 单价处理BugFix#CodeReview: 根据套餐ID从正确的数据源获取价格
// 套餐项目:从 inspection_basic_information 表获取 package_amount
// 普通项目:使用前端传入的 itemPrice已从诊疗项目获取
java.math.BigDecimal unitPrice;
Long feePackageId = activityDefinition.getFeePackageId();
if (feePackageId != null) {
// 套餐项目:查询套餐价格
InspectionPackage packageInfo = inspectionPackageService.selectPackageById(feePackageId);
if (packageInfo == null || packageInfo.getPackageAmount() == null
|| packageInfo.getPackageAmount().compareTo(java.math.BigDecimal.ZERO) <= 0) {
log.error("套餐项目 '{}' 缺少定价套餐ID: {}", itemName, feePackageId);
throw new RuntimeException("套餐项目 '" + itemName + "' 未设置有效价格,请先配置套餐金额");
}
unitPrice = packageInfo.getPackageAmount();
log.info("套餐项目 '{}' 使用套餐价格: {}", itemName, unitPrice);
} else {
// 普通项目:使用前端传入的价格
unitPrice = labApplyItemDto.getItemPrice();
if (unitPrice == null || unitPrice.compareTo(java.math.BigDecimal.ZERO) <= 0) {
log.error("检验项目 '{}' 缺少定价,无法创建医嘱", itemName);
throw new RuntimeException("检验项目 '" + itemName + "' 未设置有效价格");
}
}
adviceSaveDto.setUnitPrice(unitPrice);
// 总价处理:后端重新计算
java.math.BigDecimal totalPrice = unitPrice.multiply(adviceSaveDto.getQuantity()).setScale(2, java.math.RoundingMode.HALF_UP);
adviceSaveDto.setTotalPrice(totalPrice);
// 请求状态
adviceSaveDto.setStatusEnum(1);
// 请求类型
adviceSaveDto.setCategoryEnum(1);
// 🔧 Bug Fix #328: 请求类型设置为诊疗项目(3)避免SQL查询时被错误归类为药品
// SQL: CASE WHEN category_enum = 4 THEN 6 ELSE COALESCE(category_enum, 3) END AS advice_type
// 如果 category_enum=1则 advice_type=1(药品),导致签发时被归类为药品医嘱
adviceSaveDto.setCategoryEnum(3); // 3:诊疗项目
// 设置治疗类型(临时医嘱)
adviceSaveDto.setTherapyEnum(1); // 1:临时医嘱

View File

@@ -140,10 +140,17 @@ public class PrescriptionUtils {
/**
* 计算分组的总金额
* 🔧 Bug Fix #328: 处理 unitPrice 为 null 的情况,避免空指针异常
*/
private BigDecimal calculateTotalPrice(List<AdviceSaveDto> medicines) {
return medicines.stream().map(medicine -> medicine.getUnitPrice().multiply(medicine.getQuantity()))
.reduce(BigDecimal.ZERO, BigDecimal::add);
return medicines.stream().map(medicine -> {
BigDecimal unitPrice = medicine.getUnitPrice();
BigDecimal quantity = medicine.getQuantity();
if (unitPrice == null || quantity == null) {
return BigDecimal.ZERO;
}
return unitPrice.multiply(quantity);
}).reduce(BigDecimal.ZERO, BigDecimal::add);
}
/**
@@ -174,7 +181,10 @@ public class PrescriptionUtils {
BigDecimal currentTotal = BigDecimal.ZERO;
for (AdviceSaveDto medicine : medicines) {
// 计算单个药品总金额
BigDecimal medicinePrice = medicine.getUnitPrice().multiply(medicine.getQuantity());
// 🔧 Bug Fix #328: 处理 unitPrice 为 null 的情况,避免空指针异常
BigDecimal unitPrice = medicine.getUnitPrice();
BigDecimal quantity = medicine.getQuantity();
BigDecimal medicinePrice = (unitPrice == null || quantity == null) ? BigDecimal.ZERO : unitPrice.multiply(quantity);
// 特殊处理:单药品金额超限
if (medicinePrice.compareTo(MAX_SINGLE_PRESCRIPTION_PRICE) > 0) {
// 先保存当前组(如果有药品)
@@ -214,8 +224,13 @@ public class PrescriptionUtils {
/**
* 根据药品性质生成处方号
* 🔧 Bug Fix #328: 处理 pharmacologyCategoryCode 为 null 的情况,避免空指针异常
*/
private String generatePrescriptionNo(String pharmacologyCategoryCode) {
// null 或空字符串视为普通药品
if (pharmacologyCategoryCode == null || pharmacologyCategoryCode.isEmpty()) {
return assignSeqUtil.getSeq(AssignSeqEnum.PRESCRIPTION_COMMON_NO.getPrefix(), 8);
}
switch (pharmacologyCategoryCode) {
case "2": // 麻醉药品
return assignSeqUtil.getSeq(AssignSeqEnum.PRESCRIPTION_NARCOTIC_NO.getPrefix(), 8);

View File

@@ -1032,22 +1032,45 @@ export function getInspectionTypeList() {
}
/**
* 获取检验项目列表(从诊疗目录中筛选检验类项目)
* 获取单个检验类型详情
* @param {number} inspectionTypeId - 检验类型 ID
* @returns {Promise} { code: 200, data: InspectionType }
*/
export function getInspectionTypeDetail(inspectionTypeId) {
return request({
url: `/system/inspection-type/${inspectionTypeId}`,
method: 'get',
});
}
/**
* 获取检验项目列表(从检验项目维护表中获取)
* @param {Object} queryParams - 查询参数
* @param {string} queryParams.searchKey - 搜索关键词
* @param {number} queryParams.pageNo - 页码
* @param {number} queryParams.pageSize - 每页数量
* @param {string} queryParams.categoryCode - 目录类别编码(检验
* @param {string} queryParams.inspectionTypeId - 检验类型ID列表多个用逗号分隔
* @param {number} queryParams.inspectionTypeId - 检验类型 ID用于筛选
*/
export function getInspectionItemList(queryParams) {
return request({
url: '/data-dictionary/diagnosis-treatment/information-page',
url: '/lab/activity-definition/page',
method: 'get',
params: queryParams,
});
}
/**
* 获取检验套餐明细
* @param {number} packageId - 套餐ID (basicInformationId)
* BugFix: 修正API路径后端Controller路径为 /system/inspection-package
*/
export function getInspectionPackageDetails(packageId) {
return request({
url: `/system/inspection-package/details/${packageId}`,
method: 'get',
});
}
// ========== 会诊相关接口 ==========
/**
* 获取会诊列表

View File

@@ -2537,6 +2537,9 @@ function handleSave(prescriptionId) {
// 🔧 Bug Fix: 确保 definitionId 和 definitionDetailId 被传递(费用项必填字段)
definitionId: item.definitionId || parsedContent.definitionId,
definitionDetailId: item.definitionDetailId || parsedContent.definitionDetailId,
// 🔧 Bug Fix #328: 确保诊疗项目的执行科室(orgId)被传递,用于签发校验
orgId: item.orgId || item.positionId || parsedContent.orgId,
positionId: item.positionId || item.orgId,
// 🔧 更新 contentJson 中的 adviceType确保后端分类正确
contentJson: JSON.stringify({
...parsedContent,