147 门诊医生站》医嘱TAB页面:一次性静脉采血器为耗材系统且自动转成中成药类型,点击【保存】报错。

This commit is contained in:
Ranyunqiao
2026-03-16 10:05:55 +08:00
parent f3d011951b
commit bba63d2f1b
6 changed files with 251 additions and 25 deletions

View File

@@ -14,6 +14,8 @@ import com.core.common.utils.MessageUtils;
import com.core.common.utils.SecurityUtils; import com.core.common.utils.SecurityUtils;
import com.core.common.utils.StringUtils; import com.core.common.utils.StringUtils;
import com.core.web.util.TenantOptionUtil; 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.domain.ChargeItem;
import com.openhis.administration.service.IChargeItemService; import com.openhis.administration.service.IChargeItemService;
import com.openhis.common.constant.CommonConstants; import com.openhis.common.constant.CommonConstants;
@@ -72,6 +74,9 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
@Resource @Resource
IChargeItemService iChargeItemService; IChargeItemService iChargeItemService;
@Resource
IAccountService iAccountService;
// @Resource // @Resource
// IOrganizationLocationService iOrganizationLocationService; // IOrganizationLocationService iOrganizationLocationService;
@Resource @Resource
@@ -441,9 +446,11 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
iDeviceDispenseService.deleteDeviceDispense(adviceSaveDto.getRequestId()); iDeviceDispenseService.deleteDeviceDispense(adviceSaveDto.getRequestId());
} }
// 🔧 Bug Fix: 跳过耗材的库存校验(耗材的库存校验逻辑不同)
List<AdviceSaveDto> needCheckList = adviceSaveList.stream() List<AdviceSaveDto> needCheckList = adviceSaveList.stream()
.filter(e -> !DbOpType.DELETE.getCode().equals(e.getDbOpType()) .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()); .collect(Collectors.toList());
// 校验库存 // 校验库存
String tipRes = adviceUtils.checkInventory(needCheckList); String tipRes = adviceUtils.checkInventory(needCheckList);
@@ -778,7 +785,27 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
chargeItem.setServiceId(deviceRequest.getId()); // 医疗服务ID chargeItem.setServiceId(deviceRequest.getId()); // 医疗服务ID
chargeItem.setProductTable(adviceSaveDto.getAdviceTableName());// 产品所在表 chargeItem.setProductTable(adviceSaveDto.getAdviceTableName());// 产品所在表
chargeItem.setProductId(adviceSaveDto.getAdviceDefinitionId());// 收费项id 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.setConditionId(adviceSaveDto.getConditionId()); // 诊断id
chargeItem.setEncounterDiagnosisId(adviceSaveDto.getEncounterDiagnosisId()); // 就诊诊断id chargeItem.setEncounterDiagnosisId(adviceSaveDto.getEncounterDiagnosisId()); // 就诊诊断id
chargeItem.setDispenseId(dispenseId); // 发放ID chargeItem.setDispenseId(dispenseId); // 发放ID

View File

@@ -8,6 +8,8 @@ import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import java.math.BigDecimal; import java.math.BigDecimal;
import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.annotation.Nulls;
/** /**
* 医嘱保存 dto * 医嘱保存 dto
@@ -80,6 +82,7 @@ public class AdviceSaveDto {
private BigDecimal unitPrice; private BigDecimal unitPrice;
/** 总价 */ /** 总价 */
@JsonSetter(nulls = Nulls.AS_EMPTY)
private BigDecimal totalPrice; private BigDecimal totalPrice;
/** 费用定价主表ID */ /** 费用定价主表ID */
@@ -256,4 +259,16 @@ public class AdviceSaveDto {
this.founderOrgId = SecurityUtils.getLoginUser().getOrgId(); // 开方人科室 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;
}
}
} }

View File

@@ -174,4 +174,12 @@ public interface DoctorStationAdviceAppMapper {
List<ProofAndTestResultDto> getProofAndTestResult(@Param("encounterId") Long encounterId, List<ProofAndTestResultDto> getProofAndTestResult(@Param("encounterId") Long encounterId,
@Param("status") Integer status, @Param("typeEnum") Integer typeEnum); @Param("status") Integer status, @Param("typeEnum") Integer typeEnum);
/**
* 获取就诊的默认账户ID
*
* @param encounterId 就诊ID
* @return 默认账户ID如果没有则返回null
*/
Long getDefaultAccountId(@Param("encounterId") Long encounterId);
} }

View File

@@ -122,7 +122,7 @@
(SELECT (SELECT
DISTINCT ON (T1.ID) DISTINCT ON (T1.ID)
T1.tenant_id, T1.tenant_id,
2 AS advice_type, 4 AS advice_type,
T1.bus_no AS bus_no, T1.bus_no AS bus_no,
T1.category_code AS category_code, T1.category_code AS category_code,
'' AS pharmacology_category_code, '' AS pharmacology_category_code,
@@ -683,4 +683,13 @@
AND wad.ID = wsr.activity_id ) AND wad.ID = wsr.activity_id )
</select> </select>
<!-- 获取就诊的默认账户ID -->
<select id="getDefaultAccountId" resultType="java.lang.Long">
SELECT aa.id
FROM adm_account aa
WHERE aa.delete_flag = '0'
AND aa.encounter_id = #{encounterId}
LIMIT 1
</select>
</mapper> </mapper>

View File

@@ -120,7 +120,7 @@ function getCategoryName(row) {
} }
return '-'; return '-';
} else if (row.adviceType === 2) { // 耗材类型 } else if (row.adviceType === 2 || row.adviceType === 4) { // 🔧 Bug Fix: 耗材类型2 或 4
return '耗材'; return '耗材';
} else if (row.adviceType === 3) { // 诊疗类型 } else if (row.adviceType === 3) { // 诊疗类型
// 对于诊疗类型activityType 就是 category_code 的整数值,使用 activityType_dictText 显示 // 对于诊疗类型activityType 就是 category_code 的整数值,使用 activityType_dictText 显示

View File

@@ -22,9 +22,10 @@
@click="handleDiagnosisChange(item)" /> @click="handleDiagnosisChange(item)" />
</el-select> </el-select>
<span class="descriptions-item-label"> 费用性质 </span> <span class="descriptions-item-label"> 费用性质 </span>
<el-select v-model="accountId" placeholder="费用性质" style="width: 180px"> <el-select v-model="accountId" placeholder="费用性质" style="width: 180px"
<el-option v-for="item in contractList" :key="item.accountId" :label="item.contractName" value-key="accountId" :filterable="true">
:value="item.accountId" /> <el-option v-for="item in contractList" :key="item.accountId"
:label="item.contractName || '自费'" :value="item.accountId" />
</el-select> </el-select>
<span class="descriptions-item-label"> <span class="descriptions-item-label">
合计金额{{ totalAmount ? totalAmount.toFixed(2) : 0 }} 合计金额{{ totalAmount ? totalAmount.toFixed(2) : 0 }}
@@ -691,6 +692,7 @@ const unitMap = ref({
const buttonDisabled = computed(() => { const buttonDisabled = computed(() => {
return !props.patientInfo; return !props.patientInfo;
}); });
const props = defineProps({ const props = defineProps({
patientInfo: { patientInfo: {
type: Object, type: Object,
@@ -1245,8 +1247,33 @@ function getListInfo(addNewRow) {
priceInfo.value = res.data; priceInfo.value = res.data;
}), }),
getContract({ encounterId: props.patientInfo.encounterId }).then((res) => { getContract({ encounterId: props.patientInfo.encounterId }).then((res) => {
contractList.value = res.data; // 🔧 Bug Fix: 确保 contractList 是数组
accountId.value = props.patientInfo.accountId; 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; contentJson?.consultationRequestId;
let adviceType = item.adviceType; 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) { 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('请先选择费用性质'); proxy.$modal.msgWarning('请先选择费用性质');
return; return;
} }
@@ -1800,16 +1843,31 @@ function handleSave(prescriptionId) {
// 签发核心逻辑 // 签发核心逻辑
function executeSaveLogic() { 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) => { saveList.forEach((item) => {
item.patientId = props.patientInfo.patientId; item.patientId = props.patientInfo.patientId;
item.encounterId = props.patientInfo.encounterId; item.encounterId = props.patientInfo.encounterId;
item.accountId = props.patientInfo.accountId; item.accountId = finalAccountId;
item.dbOpType = '1'; item.dbOpType = '1';
}); });
loading.value = true; loading.value = true;
let list = saveList.map((item) => { let list = saveList.map((item) => {
// 先解析contentJson
let parsedContent = JSON.parse(item.contentJson || '{}');
// 🔧 Bug Fix: 强制将accountId设为正确的值
parsedContent.accountId = finalAccountId;
// --- 【修改点2Bug 2 修复 (单位换算)】 --- // --- 【修改点2Bug 2 修复 (单位换算)】 ---
let finalQuantity = item.quantity; let finalQuantity = item.quantity;
let finalUnitCode = item.unitCode; let finalUnitCode = item.unitCode;
@@ -1837,9 +1895,38 @@ function handleSave(prescriptionId) {
saveAdviceType = 3; // 会诊前端5 -> 后端3诊疗类 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 { return {
...JSON.parse(item.contentJson || JSON.stringify(item)), // 防止contentJson为空 ...parsedContent,
adviceType: saveAdviceType, // 使用转换后的类型 adviceType: saveAdviceType, // 使用转换后的类型
requestId: item.requestId, requestId: item.requestId,
dbOpType: '1', dbOpType: '1',
@@ -1847,7 +1934,11 @@ function handleSave(prescriptionId) {
uniqueKey: undefined, uniqueKey: undefined,
// 使用转换后的数量和单位 // 使用转换后的数量和单位
quantity: finalQuantity, 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('请先选择费用性质'); proxy.$modal.msgWarning('请先选择费用性质');
return; return;
} }
@@ -2260,17 +2352,32 @@ function handleSaveBatch(prescriptionId) {
} }
// 验证费用性质是否已选择 // 验证费用性质是否已选择
// 🔧 Bug Fix: 检查是否选择了费用性质(允许 'ZIFEI' 或 0 作为有效值)
let prescription = westernPrescriptions.value.find(p => p.id === targetPrescriptionId); let prescription = westernPrescriptions.value.find(p => p.id === targetPrescriptionId);
if (prescription) { if (prescription) {
// 同步当前选择的费用性质到处方对象 // 同步当前选择的费用性质到处方对象(保留原始值)
prescription.accountId = accountId.value; 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('请先选择费用性质'); proxy.$modal.msgWarning('请先选择费用性质');
return; 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 || []; const prescriptionExpandOrder = prescription.expandOrder || [];
if (prescriptionExpandOrder.length > 0) { if (prescriptionExpandOrder.length > 0) {
@@ -2338,6 +2445,43 @@ function handleSaveBatch(prescriptionId) {
}; };
const contentJson = JSON.stringify(itemToSave); 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 { return {
...item, ...item,
adviceType: saveAdviceType, adviceType: saveAdviceType,
@@ -2346,7 +2490,12 @@ function handleSaveBatch(prescriptionId) {
prescriptionId: targetPrescriptionId, prescriptionId: targetPrescriptionId,
contentJson: contentJson, contentJson: contentJson,
quantity: finalQuantity, 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; return;
} }
// 🔧 调试:查看保存时的完整参数
console.log('【保存】完整请求参数:', JSON.stringify(saveList, null, 2));
isSaving.value = true; isSaving.value = true;
// 本地临时更新状态,让用户立即看到保存效果 // 本地临时更新状态,让用户立即看到保存效果
@@ -2505,13 +2657,27 @@ function setValue(row) {
// 🔧 Bug #147 修复:耗材类型(adviceType=4)的专门处理 // 🔧 Bug #147 修复:耗材类型(adviceType=4)的专门处理
// 耗材从getDeviceList接口获取使用priceList中的价格 // 耗材从getDeviceList接口获取使用priceList中的价格
if (row.priceList && row.priceList.length > 0) { if (row.priceList && row.priceList.length > 0) {
prescriptionList.value[rowIndex.value].unitPrice = row.priceList[0].price; // 🔧 Bug Fix: Add validation to prevent NaN values in totalPrice
prescriptionList.value[rowIndex.value].unitTempPrice = row.priceList[0].price; const price = row.priceList[0].price;
prescriptionList.value[rowIndex.value].minUnitPrice = 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].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].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 { } else {
getOrgList(); getOrgList();
@@ -2863,7 +3029,8 @@ function getGroupMarkers() {
// 计算总价 // 计算总价
function calculateTotalPrice(row, index) { function calculateTotalPrice(row, index) {
nextTick(() => { 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)) { if (row.unitPrice !== undefined && row.unitPrice !== null && !isNaN(row.unitPrice) && isFinite(row.unitPrice)) {
row.totalPrice = (row.unitPrice * row.quantity).toFixed(6); row.totalPrice = (row.unitPrice * row.quantity).toFixed(6);