From bba63d2f1bb668b49364f36026152e079dc8200c Mon Sep 17 00:00:00 2001 From: Ranyunqiao <2499115710@qq.com> Date: Mon, 16 Mar 2026 10:05:55 +0800 Subject: [PATCH] =?UTF-8?q?147=20=E9=97=A8=E8=AF=8A=E5=8C=BB=E7=94=9F?= =?UTF-8?q?=E7=AB=99=E3=80=8B=E5=8C=BB=E5=98=B1TAB=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=EF=BC=9A=E4=B8=80=E6=AC=A1=E6=80=A7=E9=9D=99=E8=84=89=E9=87=87?= =?UTF-8?q?=E8=A1=80=E5=99=A8=E4=B8=BA=E8=80=97=E6=9D=90=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=E4=B8=94=E8=87=AA=E5=8A=A8=E8=BD=AC=E6=88=90=E4=B8=AD=E6=88=90?= =?UTF-8?q?=E8=8D=AF=E7=B1=BB=E5=9E=8B=EF=BC=8C=E7=82=B9=E5=87=BB=E3=80=90?= =?UTF-8?q?=E4=BF=9D=E5=AD=98=E3=80=91=E6=8A=A5=E9=94=99=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DoctorStationAdviceAppServiceImpl.java | 31 ++- .../web/doctorstation/dto/AdviceSaveDto.java | 15 ++ .../mapper/DoctorStationAdviceAppMapper.java | 8 + .../DoctorStationAdviceAppMapper.xml | 11 +- .../components/adviceBaseList.vue | 4 +- .../prescription/prescriptionlist.vue | 207 ++++++++++++++++-- 6 files changed, 251 insertions(+), 25 deletions(-) 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 58d66b77..5226ede8 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorStationAdviceAppServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/appservice/impl/DoctorStationAdviceAppServiceImpl.java @@ -14,6 +14,8 @@ import com.core.common.utils.MessageUtils; import com.core.common.utils.SecurityUtils; import com.core.common.utils.StringUtils; import com.core.web.util.TenantOptionUtil; +import com.openhis.administration.domain.Account; +import com.openhis.administration.service.IAccountService; import com.openhis.administration.domain.ChargeItem; import com.openhis.administration.service.IChargeItemService; import com.openhis.common.constant.CommonConstants; @@ -72,6 +74,9 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp @Resource IChargeItemService iChargeItemService; + @Resource + IAccountService iAccountService; + // @Resource // IOrganizationLocationService iOrganizationLocationService; @Resource @@ -441,9 +446,11 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp iDeviceDispenseService.deleteDeviceDispense(adviceSaveDto.getRequestId()); } + // 🔧 Bug Fix: 跳过耗材的库存校验(耗材的库存校验逻辑不同) List needCheckList = adviceSaveList.stream() .filter(e -> !DbOpType.DELETE.getCode().equals(e.getDbOpType()) - && !ItemType.ACTIVITY.getValue().equals(e.getAdviceType())) + && !ItemType.ACTIVITY.getValue().equals(e.getAdviceType()) + && !ItemType.DEVICE.getValue().equals(e.getAdviceType())) // 排除耗材 .collect(Collectors.toList()); // 校验库存 String tipRes = adviceUtils.checkInventory(needCheckList); @@ -778,7 +785,27 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp chargeItem.setServiceId(deviceRequest.getId()); // 医疗服务ID chargeItem.setProductTable(adviceSaveDto.getAdviceTableName());// 产品所在表 chargeItem.setProductId(adviceSaveDto.getAdviceDefinitionId());// 收费项id - chargeItem.setAccountId(adviceSaveDto.getAccountId());// 关联账户ID + // 🔧 Bug Fix: 如果accountId为null,从就诊中获取账户ID,如果没有则自动创建 + Long accountId = adviceSaveDto.getAccountId(); + if (accountId == null) { + // 尝试从患者就诊中获取默认账户ID(自费账户) + Account selfAccount = iAccountService.getSelfAccount(adviceSaveDto.getEncounterId()); + if (selfAccount != null) { + accountId = selfAccount.getId(); + } else { + // 自动创建自费账户 + Account newAccount = new Account(); + newAccount.setPatientId(adviceSaveDto.getPatientId()); + newAccount.setEncounterId(adviceSaveDto.getEncounterId()); + newAccount.setContractNo(CommonConstants.BusinessName.DEFAULT_CONTRACT_NO); + newAccount.setTypeCode(AccountType.PERSONAL_CASH_ACCOUNT.getCode()); + newAccount.setBalanceAmount(BigDecimal.ZERO); + newAccount.setStatusEnum(AccountStatus.ACTIVE.getValue()); + newAccount.setEncounterFlag(Whether.YES.getValue()); + accountId = iAccountService.saveAccountByRegister(newAccount); + } + } + chargeItem.setAccountId(accountId);// 关联账户ID chargeItem.setConditionId(adviceSaveDto.getConditionId()); // 诊断id chargeItem.setEncounterDiagnosisId(adviceSaveDto.getEncounterDiagnosisId()); // 就诊诊断id chargeItem.setDispenseId(dispenseId); // 发放ID diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/dto/AdviceSaveDto.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/dto/AdviceSaveDto.java index 95d0d774..0b973fad 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/dto/AdviceSaveDto.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/doctorstation/dto/AdviceSaveDto.java @@ -8,6 +8,8 @@ import lombok.Data; import lombok.experimental.Accessors; import java.math.BigDecimal; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; /** * 医嘱保存 dto @@ -80,6 +82,7 @@ public class AdviceSaveDto { private BigDecimal unitPrice; /** 总价 */ + @JsonSetter(nulls = Nulls.AS_EMPTY) private BigDecimal totalPrice; /** 费用定价主表ID */ @@ -256,4 +259,16 @@ public class AdviceSaveDto { this.founderOrgId = SecurityUtils.getLoginUser().getOrgId(); // 开方人科室 } + /** + * 🔧 Bug Fix: Custom setter for totalPrice to handle NaN and invalid values + * Prevents JSON parse errors when frontend sends NaN or invalid BigDecimal values + */ + public void setTotalPrice(BigDecimal totalPrice) { + if (totalPrice == null || totalPrice.compareTo(BigDecimal.ZERO) < 0) { + this.totalPrice = BigDecimal.ZERO; + } else { + this.totalPrice = totalPrice; + } + } + } 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 65e4c29e..1ff53bff 100644 --- 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 @@ -174,4 +174,12 @@ public interface DoctorStationAdviceAppMapper { List getProofAndTestResult(@Param("encounterId") Long encounterId, @Param("status") Integer status, @Param("typeEnum") Integer typeEnum); + /** + * 获取就诊的默认账户ID + * + * @param encounterId 就诊ID + * @return 默认账户ID,如果没有则返回null + */ + Long getDefaultAccountId(@Param("encounterId") Long encounterId); + } 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 8406580f..87053af5 100644 --- a/openhis-server-new/openhis-application/src/main/resources/mapper/doctorstation/DoctorStationAdviceAppMapper.xml +++ b/openhis-server-new/openhis-application/src/main/resources/mapper/doctorstation/DoctorStationAdviceAppMapper.xml @@ -122,7 +122,7 @@ (SELECT DISTINCT ON (T1.ID) T1.tenant_id, - 2 AS advice_type, + 4 AS advice_type, T1.bus_no AS bus_no, T1.category_code AS category_code, '' AS pharmacology_category_code, @@ -683,4 +683,13 @@ AND wad.ID = wsr.activity_id ) + + + \ No newline at end of file diff --git a/openhis-ui-vue3/src/views/doctorstation/components/adviceBaseList.vue b/openhis-ui-vue3/src/views/doctorstation/components/adviceBaseList.vue index 456faaf7..0cb0eed9 100644 --- a/openhis-ui-vue3/src/views/doctorstation/components/adviceBaseList.vue +++ b/openhis-ui-vue3/src/views/doctorstation/components/adviceBaseList.vue @@ -103,7 +103,7 @@ const medicalInsuranceLevelMap = { // 获取药品分类名称 - 使用系统统一的数据字典 function getCategoryName(row) { if (!row) return '-'; - + if (row.adviceType === 1) { // 药品类型 // 优先使用系统统一的药品分类数据字典 if (med_category_code.value && med_category_code.value.length > 0) { @@ -120,7 +120,7 @@ function getCategoryName(row) { } return '-'; - } else if (row.adviceType === 2) { // 耗材类型 + } else if (row.adviceType === 2 || row.adviceType === 4) { // 🔧 Bug Fix: 耗材类型(2 或 4) return '耗材'; } else if (row.adviceType === 3) { // 诊疗类型 // 对于诊疗类型,activityType 就是 category_code 的整数值,使用 activityType_dictText 显示 diff --git a/openhis-ui-vue3/src/views/doctorstation/components/prescription/prescriptionlist.vue b/openhis-ui-vue3/src/views/doctorstation/components/prescription/prescriptionlist.vue index 7816fed5..c3034a1b 100644 --- a/openhis-ui-vue3/src/views/doctorstation/components/prescription/prescriptionlist.vue +++ b/openhis-ui-vue3/src/views/doctorstation/components/prescription/prescriptionlist.vue @@ -22,9 +22,10 @@ @click="handleDiagnosisChange(item)" /> 费用性质: - - + + 合计金额:{{ totalAmount ? totalAmount.toFixed(2) : 0 }}元 @@ -691,6 +692,7 @@ const unitMap = ref({ const buttonDisabled = computed(() => { return !props.patientInfo; }); + const props = defineProps({ patientInfo: { type: Object, @@ -1245,8 +1247,33 @@ function getListInfo(addNewRow) { priceInfo.value = res.data; }), getContract({ encounterId: props.patientInfo.encounterId }).then((res) => { - contractList.value = res.data; - accountId.value = props.patientInfo.accountId; + // 🔧 Bug Fix: 确保 contractList 是数组 + let contractData = res.data; + if (!contractData || !Array.isArray(contractData)) { + contractData = []; + } + + // 🔧 Bug Fix: 处理返回的费用性质数据,如果没有contractName或为空,显示"自费" + if (contractData.length > 0) { + contractList.value = contractData.map(item => { + // 如果 contractName 为空/undefined/null,显示"自费" + const contractName = item.contractName; + if (!contractName || contractName === '' || contractName === null) { + return { ...item, contractName: '自费' }; + } + return item; + }); + } else { + // 没有数据时添加默认"自费"选项 + contractList.value = [{ accountId: 0, contractName: '自费' }]; + } + + // 设置默认选中的费用性质 + // 优先使用 contractList 中的第一个选项 + if (contractList.value.length > 0) { + accountId.value = contractList.value[0].accountId; + } + console.log('费用性质数据:', contractList.value, '选中:', accountId.value); }) ]); @@ -1267,7 +1294,15 @@ function getListInfo(addNewRow) { contentJson?.consultationRequestId; let adviceType = item.adviceType; - let adviceType_dictText = item.adviceType_dictText || mapAdviceTypeLabel(item.adviceType); + + // 🔧 Bug Fix: 后端保存时将耗材(4)转换为中成药(2),显示时需要转换回来 + // 检查 adviceTableName,如果是耗材表则应该是耗材类型 + const adviceTableName = contentJson?.adviceTableName || item.adviceTableName; + if (adviceType === 2 && adviceTableName === 'adm_device_definition') { + adviceType = 4; // 后端2(中成药) -> 前端4(耗材) + } + + let adviceType_dictText = item.adviceType_dictText || mapAdviceTypeLabel(adviceType); // 如果是会诊类型,设置为会诊类型 if (isConsultation) { @@ -1716,7 +1751,15 @@ function handleSave(prescriptionId) { } // 验证费用性质是否已选择 - if (!accountId.value) { + // 🔧 Bug Fix: 允许 'ZIFEI' 作为有效值 + if (!accountId.value && accountId.value !== 'ZIFEI') { + proxy.$modal.msgWarning('请先选择费用性质'); + return; + } + + // 验证费用性质是否已选择 + // 🔧 Bug Fix: 允许 'ZIFEI' 或 0 作为有效值 + if (!accountId.value && accountId.value !== 'ZIFEI' && accountId.value !== 0) { proxy.$modal.msgWarning('请先选择费用性质'); return; } @@ -1800,16 +1843,31 @@ function handleSave(prescriptionId) { // 签发核心逻辑 function executeSaveLogic() { + // 🔧 Bug Fix: 获取当前选中的费用性质,如果是'ZIFEI'或0则转为null,让后端查询默认账户 + let finalAccountId = accountId.value; + if (finalAccountId === 'ZIFEI' || finalAccountId === 0) { + finalAccountId = null; + } else if (finalAccountId && !isNaN(Number(finalAccountId))) { + finalAccountId = Number(finalAccountId); + } else { + finalAccountId = null; + } + saveList.forEach((item) => { item.patientId = props.patientInfo.patientId; item.encounterId = props.patientInfo.encounterId; - item.accountId = props.patientInfo.accountId; + item.accountId = finalAccountId; item.dbOpType = '1'; }); loading.value = true; let list = saveList.map((item) => { + // 先解析contentJson + let parsedContent = JSON.parse(item.contentJson || '{}'); + // 🔧 Bug Fix: 强制将accountId设为正确的值 + parsedContent.accountId = finalAccountId; + // --- 【修改点2:Bug 2 修复 (单位换算)】 --- let finalQuantity = item.quantity; let finalUnitCode = item.unitCode; @@ -1837,9 +1895,38 @@ function handleSave(prescriptionId) { saveAdviceType = 3; // 会诊:前端5 -> 后端3(诊疗类) } + // 🔧 Bug Fix: Validate and fix NaN values before sending to backend + if (item.totalPrice === undefined || item.totalPrice === null || + (typeof item.totalPrice === 'number' && (isNaN(item.totalPrice) || !isFinite(item.totalPrice)))) { + item.totalPrice = '0.00'; + console.warn('Fixed NaN totalPrice for item:', item.adviceName); + } + // 构造请求参数 + // 🔧 Bug Fix: 确保库存匹配成功的关键字段 + // 耗材使用 adm_device_definition 表 + let adviceTableNameVal = ''; + let locationIdVal = null; + if (item.adviceType == 4) { + adviceTableNameVal = 'adm_device_definition'; + locationIdVal = item.locationId || item.positionId; + } else { + adviceTableNameVal = item.adviceTableName || ''; + locationIdVal = item.locationId || item.positionId; + } + + console.log('保存医嘱参数:', { + adviceName: item.adviceName, + adviceType: item.adviceType, + saveAdviceType: saveAdviceType, + adviceTableName: adviceTableNameVal, + locationId: locationIdVal, + positionId: item.positionId, + locationId2: item.locationId + }); + return { - ...JSON.parse(item.contentJson || JSON.stringify(item)), // 防止contentJson为空 + ...parsedContent, adviceType: saveAdviceType, // 使用转换后的类型 requestId: item.requestId, dbOpType: '1', @@ -1847,7 +1934,11 @@ function handleSave(prescriptionId) { uniqueKey: undefined, // 使用转换后的数量和单位 quantity: finalQuantity, - unitCode: finalUnitCode + unitCode: finalUnitCode, + totalPrice: item.totalPrice, // Ensure totalPrice is valid + // 🔧 Bug Fix: 确保库存匹配成功的关键字段 + adviceTableName: adviceTableNameVal, + locationId: locationIdVal }; }); @@ -2076,7 +2167,8 @@ function handleSaveSign(row, index, prescriptionId) { } // 验证费用性质是否已选择 - if (!accountId.value) { + // 🔧 Bug Fix: 允许 'ZIFEI' 或 0 作为有效值 + if (!accountId.value && accountId.value !== 'ZIFEI' && accountId.value !== 0) { proxy.$modal.msgWarning('请先选择费用性质'); return; } @@ -2260,17 +2352,32 @@ function handleSaveBatch(prescriptionId) { } // 验证费用性质是否已选择 + // 🔧 Bug Fix: 检查是否选择了费用性质(允许 'ZIFEI' 或 0 作为有效值) let prescription = westernPrescriptions.value.find(p => p.id === targetPrescriptionId); if (prescription) { - // 同步当前选择的费用性质到处方对象 + // 同步当前选择的费用性质到处方对象(保留原始值) prescription.accountId = accountId.value; } - if (!prescription || !prescription.accountId) { + // 🔧 Bug Fix: 允许 'ZIFEI' 或 0 作为有效值 + if (!prescription || (!prescription.accountId && prescription.accountId !== 'ZIFEI' && prescription.accountId !== 0)) { proxy.$modal.msgWarning('请先选择费用性质'); return; } + // 🔧 Bug Fix: 在保存时才转换 accountId + let finalAccountId = accountId.value; + if (finalAccountId === 'ZIFEI') { + finalAccountId = null; + } else if (finalAccountId && !isNaN(Number(finalAccountId))) { + finalAccountId = Number(finalAccountId); + } + + // 更新到处方对象 + if (prescription) { + prescription.accountId = finalAccountId; + } + // 获取对应处方的展开状态 const prescriptionExpandOrder = prescription.expandOrder || []; if (prescriptionExpandOrder.length > 0) { @@ -2338,6 +2445,43 @@ function handleSaveBatch(prescriptionId) { }; const contentJson = JSON.stringify(itemToSave); + // 🔧 Bug Fix: 处理accountId,如果是'ZIFEI'或0则转为null,让后端查询默认账户 + let itemAccountId = finalAccountId; + if (itemAccountId === 'ZIFEI' || itemAccountId === 0) { + itemAccountId = null; + } else if (itemAccountId && !isNaN(Number(itemAccountId))) { + itemAccountId = Number(itemAccountId); + } + + // 🔧 Bug Fix: 确保库存匹配成功的关键字段 + let adviceTableNameVal = ''; + let locationIdVal = null; + + console.log('耗材 debug:', { + adviceType: item.adviceType, + positionId: item.positionId, + locationId: item.locationId, + adviceDefinitionId: item.adviceDefinitionId, + adviceTableName: item.adviceTableName, + // 完整对象 + item: item + }); + + if (item.adviceType == 4) { + adviceTableNameVal = 'adm_device_definition'; + locationIdVal = item.locationId || item.positionId; + } else { + adviceTableNameVal = item.adviceTableName || ''; + locationIdVal = item.locationId || item.positionId; + } + + console.log('处理后:', { + adviceTableName: adviceTableNameVal, + locationId: locationIdVal, + adviceDefinitionId: item.adviceDefinitionId, + adviceDefinitionIdType: typeof item.adviceDefinitionId + }); + return { ...item, adviceType: saveAdviceType, @@ -2346,7 +2490,12 @@ function handleSaveBatch(prescriptionId) { prescriptionId: targetPrescriptionId, contentJson: contentJson, quantity: finalQuantity, - unitCode: finalUnitCode + unitCode: finalUnitCode, + // 🔧 Bug Fix: 显式设置accountId + accountId: itemAccountId, + // 🔧 Bug Fix: 确保库存匹配成功 + adviceTableName: adviceTableNameVal, + locationId: locationIdVal }; }); // --- 【修改结束】 --- @@ -2361,6 +2510,9 @@ function handleSaveBatch(prescriptionId) { return; } + // 🔧 调试:查看保存时的完整参数 + console.log('【保存】完整请求参数:', JSON.stringify(saveList, null, 2)); + isSaving.value = true; // 本地临时更新状态,让用户立即看到保存效果 @@ -2505,13 +2657,27 @@ function setValue(row) { // 🔧 Bug #147 修复:耗材类型(adviceType=4)的专门处理 // 耗材从getDeviceList接口获取,使用priceList中的价格 if (row.priceList && row.priceList.length > 0) { - prescriptionList.value[rowIndex.value].unitPrice = row.priceList[0].price; - prescriptionList.value[rowIndex.value].unitTempPrice = row.priceList[0].price; - prescriptionList.value[rowIndex.value].minUnitPrice = row.priceList[0].price; + // 🔧 Bug Fix: Add validation to prevent NaN values in totalPrice + const price = row.priceList[0].price; + const validPrice = (price !== undefined && price !== null && !isNaN(price) && isFinite(price)) + ? Number(price) + : 0; + + prescriptionList.value[rowIndex.value].unitPrice = validPrice; + prescriptionList.value[rowIndex.value].unitTempPrice = validPrice; + prescriptionList.value[rowIndex.value].minUnitPrice = validPrice; prescriptionList.value[rowIndex.value].quantity = 1; - prescriptionList.value[rowIndex.value].totalPrice = row.priceList[0].price; + prescriptionList.value[rowIndex.value].totalPrice = validPrice; prescriptionList.value[rowIndex.value].positionName = row.positionName || ''; - prescriptionList.value[rowIndex.value].locationId = row.positionId; + // 🔧 Bug Fix: 使用 positionId,如果为空则使用患者信息中的 orgId + console.log('设置耗材locationId:', { + rowPositionId: row.positionId, + patientOrgId: props.patientInfo.orgId, + patientInfo: props.patientInfo + }); + const finalLocationId = row.positionId || props.patientInfo.orgId; + prescriptionList.value[rowIndex.value].locationId = finalLocationId; + prescriptionList.value[rowIndex.value].positionId = finalLocationId; } } else { getOrgList(); @@ -2863,7 +3029,8 @@ function getGroupMarkers() { // 计算总价 function calculateTotalPrice(row, index) { nextTick(() => { - if (row.adviceType == 3) { + // 🔧 Bug Fix: Handle consumables (adviceType=4) and projects (adviceType=3) the same way + if (row.adviceType == 3 || row.adviceType == 4) { // 检查价格是否为有效数字 if (row.unitPrice !== undefined && row.unitPrice !== null && !isNaN(row.unitPrice) && isFinite(row.unitPrice)) { row.totalPrice = (row.unitPrice * row.quantity).toFixed(6);