372 【住院医生站】医嘱录入执行科室显示ID乱码,且缺乏动态匹配逻辑

373 【住院医生站】医嘱搜索缺失“II级护理”项目(与诊疗目录配置不符)
This commit is contained in:
2026-04-22 15:35:25 +08:00
parent d663c46422
commit f125c8dc85
8 changed files with 213 additions and 39 deletions

View File

@@ -53,7 +53,7 @@ public class DoctorStationAdviceController {
@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize,
@RequestParam(value = "categoryCode", required = false) String categoryCode) {
return R.ok(iDoctorStationAdviceAppService.getAdviceBaseInfo(adviceBaseDto, searchKey, locationId,
adviceDefinitionIdParamList, organizationId, pageNo, pageSize, Whether.NO.getValue(), adviceTypes, null, categoryCode));
adviceDefinitionIdParamList, organizationId, pageNo, pageSize, null, adviceTypes, null, categoryCode));
}
/**

View File

@@ -118,7 +118,7 @@ public class DoctorStationChineseMedicalController {
organizationId = SecurityUtils.getLoginUser().getOrgId();
}
return R.ok(iDoctorStationChineseMedicalAppService.getTcmAdviceBaseInfo(adviceBaseDto, searchKey, locationId,
adviceDefinitionIdParamList, organizationId, pageNo, pageSize, Whether.NO.getValue()));
adviceDefinitionIdParamList, organizationId, pageNo, pageSize, null));
}
/**

View File

@@ -198,6 +198,7 @@ public class AdviceBaseDto {
/**
* 所属科室
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long orgId;
/**

View File

@@ -819,7 +819,8 @@ function selectAdviceBase(key, row) {
combinationList.value[rowIndex.value].positionName = stock.locationName;
}
} else {
combinationList.value[rowIndex.value].orgId = JSON.parse(JSON.stringify(row)).positionId;
// 🔧 修复执行科室逻辑:优先使用项目维护的所属科室(row.orgId),其次使用positionId
combinationList.value[rowIndex.value].orgId = row.orgId || JSON.parse(JSON.stringify(row)).positionId;
combinationList.value[rowIndex.value].unitPrice = row.priceList[0].price;
}
expandOrder.value = [key];

View File

@@ -702,7 +702,14 @@ async function selectAdviceBase(key, row) {
};
// 后续字段处理保持原样
prescriptionList.value[rowIndex.value].orgId = undefined;
// 🔧 修复执行科室逻辑:诊疗项目优先使用项目维护的所属科室(row.orgId)
// 如果所属科室为空,则回退到患者当前所在科室
if (row.adviceType === 3 && row.orgId) {
// 项目有所属科室,直接使用
prescriptionList.value[rowIndex.value].orgId = row.orgId;
} else {
prescriptionList.value[rowIndex.value].orgId = undefined;
}
prescriptionList.value[rowIndex.value].dose = undefined;
prescriptionList.value[rowIndex.value].unitCodeList = unitCodeList.value;
prescriptionList.value[rowIndex.value].doseUnitCode =

View File

@@ -3305,8 +3305,9 @@ function syncGroupFields(row) {
}
// 同步执行科室
if (row.positionId || row.orgId) {
prescriptionList.value[rowIndex.value].orgId = row.positionId || row.orgId;
if (row.orgId || row.positionId) {
// 🔧 修复:优先使用项目所属科室(orgId)其次positionId
prescriptionList.value[rowIndex.value].orgId = row.orgId || row.positionId;
}
// 同步皮试标记
@@ -3390,9 +3391,8 @@ function setValue(row) {
showPopover: false, // 确保查询框关闭
};
console.log('[BugFix] setValue - prescriptionList[rowIndex].adviceType_dictText:', prescriptionList.value[rowIndex.value].adviceType_dictText);
// 🔧 Bug #218 修复保留组套中的值不要强制设为undefined
// 只有当值未定义时才使用默认值
prescriptionList.value[rowIndex.value].orgId = row.positionId || row.orgId;
// 🔧 修复执行科室逻辑:优先使用项目维护的所属科室(row.orgId)其次使用positionId最后回退到患者科室
prescriptionList.value[rowIndex.value].orgId = row.orgId || row.positionId || props.patientInfo?.orgId;
prescriptionList.value[rowIndex.value].dose = row.dose || row.doseQuantity;
prescriptionList.value[rowIndex.value].quantity = row.quantity || 1;
prescriptionList.value[rowIndex.value].unitCodeList = unitCodeList.value;
@@ -3619,8 +3619,8 @@ function handleSaveGroup(orderGroupList) {
unitCode: item.unitCode,
unitCode_dictText: item.unitCodeName || '',
statusEnum: 1,
// 🔧 Bug #218 修复:优先使用 item.positionId其次使用 orderDetailInfos.positionId
orgId: item.positionId || item.orderDetailInfos?.positionId || mergedDetail.positionId,
// 🔧 修复执行科室逻辑:优先使用 orgId(所属科室),其次 positionId
orgId: item.orderDetailInfos?.orgId || mergedDetail.orgId || item.positionId || item.orderDetailInfos?.positionId || mergedDetail.positionId,
dbOpType: prescriptionList.value[rowIndex.value].requestId ? '2' : '1',
conditionId: conditionId.value,
conditionDefinitionId: conditionDefinitionId.value,

View File

@@ -395,11 +395,12 @@
clearable
v-model="row.orgId"
style="width: 200px"
:data="config.organization"
:data="orgTreeData"
:props="{ value: 'id', label: 'name', children: 'children' }"
value-key="id"
check-strictly
default-expand-all
:fallback-option="orgFallbackOption"
@change="handleOrgChange"
placeholder="请选择执行科室"
/>
@@ -422,7 +423,7 @@
</template>
<script setup lang="ts">
import {getCurrentInstance, nextTick, onMounted, ref, watch} from 'vue';
import {computed, getCurrentInstance, nextTick, onMounted, ref, watch} from 'vue';
interface Config {
diagnosisName: string; // 仅用于显示
@@ -569,6 +570,54 @@ const handleCancel = () => {
const handleNumberClick = (item: any) =>
props.handlers.handleNumberClick(item, props.index, props.row);
const handleOrgChange = (value: any) => props.handlers.handleOrgChange(value, props.index);
// 🔧 关键修复:计算属性,确保当前行的 orgId 始终存在于树数据中
// 当 orgId 不在科室树中时,注入一个临时节点,让 el-tree-select 能正确显示中文名称
const orgTreeData = computed(() => {
const tree = props.config.organization || [];
const orgId = props.row?.orgId;
const orgName = props.row?.orgName || props.row?.positionName;
if (!orgId || !orgName) return tree;
// 检查 orgId 是否已在树中存在
const existsInTree = findOrgName(orgId);
if (existsInTree) return tree;
// 注入临时节点到树根层级
return [...tree, { id: String(orgId), name: orgName, children: [] }];
});
// 🔧 从 organization 树中根据 orgId 查找科室名称(供 fallback-option 使用)
function findOrgName(orgId: any): string {
if (!orgId || !props.config.organization || props.config.organization.length === 0) return '';
const strId = String(orgId);
function walk(nodes: any[]): string {
if (!nodes) return '';
for (const node of nodes) {
if (String(node.id) === strId) return node.name;
// 模糊匹配:处理大 Long 精度丢失的情况
if (typeof node.id === 'string' && node.id.length >= 16 && strId.length >= 16
&& node.id.substring(0, 15) === strId.substring(0, 15)) {
return node.name;
}
if (node.children) {
const found = walk(node.children);
if (found) return found;
}
}
return '';
}
return walk(props.config.organization);
}
// 🔧 el-tree-select 的 fallback-option当 orgId 匹配不到树节点时,
// 仍然显示对应的中文科室名称而非原始数字 ID
// 优先从科室树查找,其次用 row 上保存的 orgName/positionName 兜底
const orgFallbackOption = (value: any) => {
const name = findOrgName(value);
const fallbackName = props.row?.orgName || props.row?.positionName;
return { label: name || fallbackName || value, value };
};
const convertValues = () => props.handlers.convertValue('doseQuantity', props.row, props.index);
const convertDoseValues = () => props.handlers.convertValue('dose', props.row, props.index);
const calculateTotalPrice = () => props.handlers.calculateTotal('price', props.row, props.index);

View File

@@ -593,29 +593,45 @@ function getListInfo(addNewRow) {
}, 180);
isAdding.value = false;
expandOrder.value = [];
// 🔧 修复:先加载科室树,再处理处方数据
// 确保 resolveOrgId 在 organization 树已加载的情况下执行
// 避免因树为空导致 orgId 无法匹配,从而显示数字 ID 而非中文名称
const orgTreePromise = getOrgTree().then((res) => {
organization.value = res?.data?.records ?? res?.data ?? [];
});
getPrescriptionList(patientInfo.value.encounterId).then((res) => {
console.log('getListInfo==========>', JSON.stringify(res.data));
loadingInstance.close();
prescriptionList.value = res.data
.map((item) => {
const parsedContent = JSON.parse(item.contentJson);
return {
...parsedContent,
...item,
doseQuantity: parsedContent?.doseQuantity,
doseUnitCode_dictText: parsedContent?.doseUnitCode_dictText,
// 确保 therapyEnum 被正确设置,优先使用 contentJson 中的值
therapyEnum: String(parsedContent?.therapyEnum ?? item.therapyEnum ?? '1'),
};
})
.sort((a, b) => {
return new Date(b.requestTime) - new Date(a.requestTime);
});
getGroupMarkers(); // 更新标记
if (props.activeTab == 'prescription' && addNewRow) {
handleAddPrescription();
}
// 等待科室树加载完成后再处理处方数据,确保 resolveOrgId 能正确匹配
orgTreePromise.then(() => {
loadingInstance.close();
prescriptionList.value = res.data
.map((item) => {
const parsedContent = JSON.parse(item.contentJson);
return {
...parsedContent,
...item,
doseQuantity: parsedContent?.doseQuantity,
doseUnitCode_dictText: parsedContent?.doseUnitCode_dictText,
// 确保 therapyEnum 被正确设置,优先使用 contentJson 中的值
therapyEnum: String(parsedContent?.therapyEnum ?? item.therapyEnum ?? '1'),
// 🔧 修复:确保 orgId 为 String 类型,与 organization 树的 id 类型一致
// 关键:优先使用 item.positionId后端 @JsonSerialize 保证精度),
// 而非 parsedContent.orgId来自 JSON.parse大 Long 可能精度丢失)
// 使用 resolveOrgId 从组织树中匹配正确的 String id
orgId: resolveOrgId(item.positionId || parsedContent?.orgId || item.orgId) || undefined,
// 🔧 修复:同时保存 orgName当 orgId 在科室树中匹配不到时作为兜底显示
// 优先从科室树查找名称,其次用 positionName后端已保存的科室名最后用 contentJson 中的 orgName
orgName: findOrgName(item.positionId || parsedContent?.orgId || item.orgId) || item.positionName || parsedContent?.orgName || undefined,
};
})
.sort((a, b) => {
return new Date(b.requestTime) - new Date(a.requestTime);
});
getGroupMarkers(); // 更新标记
if (props.activeTab == 'prescription' && addNewRow) {
handleAddPrescription();
}
});
});
getContract({ encounterId: patientInfo.value.encounterId }).then((res) => {
contractList.value = res.data;
@@ -796,6 +812,27 @@ function clickRowDb(row, column, event) {
}
}
/**
* 🔧 从 organization 树中根据 orgId 查找科室名称
* 供 el-tree-select 的 fallback-option 使用
*/
function findOrgName(orgId) {
if (!orgId || !organization.value || organization.value.length === 0) return '';
const strId = String(orgId);
function walk(nodes) {
if (!nodes) return '';
for (const node of nodes) {
if (String(node.id) === strId) return node.name;
if (node.children) {
const found = walk(node.children);
if (found) return found;
}
}
return '';
}
return walk(organization.value);
}
function getFormConfig() {
return {
diagnosisName: diagnosisName.value,
@@ -934,7 +971,74 @@ function selectAdviceBase(key, row) {
function getOrgList() {
getOrgTree().then((res) => {
organization.value = res.data.records;
organization.value = res?.data?.records ?? res?.data ?? [];
// 🔧 修复组织树加载完成后重新解析所有医嘱行的orgId
// 确保orgId能匹配到树节点解决精度丢失或类型不匹配的问题
resolveAllOrgIds();
});
}
/**
* 🔧 从 organization 树中查找正确的 String 类型 id
* 解决 JavaScript 大整数精度丢失问题:
* 当 orgId 为 Number 且超过 Number.MAX_SAFE_INTEGER 时String() 转换会丢失精度
* 例19072662242626273 (Long) → JS Number 19072662242626272 → String "19072662242626272" (错误)
* 通过在 organization 树中模糊匹配找到正确的 String id
*/
function resolveOrgId(rawOrgId) {
if (!rawOrgId) return undefined;
const strOrgId = String(rawOrgId);
// 递归在树中查找匹配的节点
function findInTree(nodes) {
if (!nodes || !Array.isArray(nodes)) return null;
for (const node of nodes) {
// 精确匹配正常情况id 和 orgId 都是 String 且值一致)
if (String(node.id) === strOrgId) return String(node.id);
// 模糊匹配精度丢失通常只影响最后1-2位数字
// 比较前15位是否相同2^53 约 9007199254740992共16位前15位稳定
// 🔧 修复:同时处理 Number 和 String 类型的精度丢失情况
// 场景1rawOrgId 是 NumberJS已丢失精度node.id 是正确 String
// 场景2rawOrgId 是 String从已丢失精度的 Number 转来node.id 是正确 String
if (typeof node.id === 'string' && node.id.length >= 16 && strOrgId.length >= 16
&& node.id.substring(0, 15) === strOrgId.substring(0, 15)) {
return String(node.id);
}
// 递归搜索子节点
if (node.children) {
const found = findInTree(node.children);
if (found) return found;
}
}
return null;
}
const resolved = findInTree(organization.value);
if (!resolved) {
console.warn(`🔧 resolveOrgId 未匹配: rawOrgId=${rawOrgId}, type=${typeof rawOrgId}, strOrgId=${strOrgId}, treeSize=${organization.value?.length}`);
}
return resolved || strOrgId;
}
/**
* 🔧 组织树加载完成后重新解析所有医嘱行的orgId
* 确保已有的医嘱行 orgId 能匹配到树节点
*/
function resolveAllOrgIds() {
if (!organization.value || organization.value.length === 0) return;
prescriptionList.value.forEach((row) => {
if (row.orgId && row.adviceType == 3) {
const resolved = resolveOrgId(row.orgId);
if (resolved !== String(row.orgId)) {
console.log(`🔧 orgId精度修复: ${row.orgId}${resolved} (${row.adviceName})`);
row.orgId = resolved;
}
// 🔧 修复:同步更新 orgName确保科室树加载后能正确显示中文名称
const treeName = findOrgName(row.orgId);
if (treeName) {
row.orgName = treeName;
}
}
});
}
@@ -1000,6 +1104,9 @@ function handleNumberClick(item, index, row) {
// 选择执行科室处理
function handleOrgChange(value, index) {
prescriptionList.value[index].positionId = value;
prescriptionList.value[index].orgId = value;
// 🔧 修复:同步保存 orgName确保树匹配不到时仍有中文名称可显示
prescriptionList.value[index].orgName = findOrgName(value) || '';
}
/**
@@ -1090,11 +1197,11 @@ function handleSave() {
// 保存签发按钮
isSaving.value = true;
console.log('签发处方参数:', {
organizationId: patientInfo.value.orgId,
organizationId: patientInfo.value.inHospitalOrgId,
adviceSaveList: list,
});
savePrescriptionSign({
organizationId: patientInfo.value.orgId,
organizationId: patientInfo.value.inHospitalOrgId,
regAdviceSaveList: list,
})
.then((res) => {
@@ -1391,7 +1498,14 @@ function setValue(row) {
...prevRow,
...baseRow,
uniqueKey: currentUniqueKey, // 确保 uniqueKey 不被覆盖
orgId: row.adviceType != 3 ? undefined : JSON.parse(JSON.stringify(row)).positionId,
// 🔧 修复执行科室逻辑:
// 1. 非诊疗类型(adviceType!=3)不需要执行科室设为undefined
// 2. 诊疗类型优先使用项目维护的所属科室(row.orgId)其次positionId
// 3. 如果都为空,回退到患者当前所在科室(patientInfo.orgId)
// 4. 使用 resolveOrgId 从组织树中匹配正确的 String id解决大 Long 精度丢失问题
orgId: row.adviceType != 3 ? undefined : (resolveOrgId(row.orgId || row.positionId || patientInfo.value?.inHospitalOrgId) || ''),
// 🔧 修复:同时保存 orgName当 orgId 在科室树中匹配不到时作为兜底显示
orgName: row.adviceType != 3 ? undefined : (findOrgName(row.orgId || row.positionId || patientInfo.value?.inHospitalOrgId) || row.orgName || patientInfo.value?.inHospitalOrgName || ''),
dose: undefined,
unitCodeList: unitCodeList.value,
doseUnitCode: row.doseUnitCode,
@@ -1487,7 +1601,9 @@ function handleSaveGroup(orderGroupList) {
unitCode: item.unitCode,
unitCode_dictText: item.unitCodeName || '',
statusEnum: 1,
orgId: item.orderDetailInfos?.positionId || mergedDetail.positionId,
orgId: resolveOrgId(item.orderDetailInfos?.orgId || mergedDetail.orgId || patientInfo.value?.inHospitalOrgId) || '',
// 🔧 修复:同时保存 orgName确保树匹配不到时仍有中文名称可显示
orgName: findOrgName(item.orderDetailInfos?.orgId || mergedDetail.orgId || patientInfo.value?.inHospitalOrgId) || item.orderDetailInfos?.orgName || mergedDetail.orgName || patientInfo.value?.inHospitalOrgName || '',
dbOpType: prescriptionList.value[rowIndex.value].requestId ? '2' : '1',
conditionId: conditionId.value,
conditionDefinitionId: conditionDefinitionId.value,