Files
his/docs/bug439_analysis.md
2026-05-16 14:18:41 +08:00

6.0 KiB
Raw Blame History

Bug #439 分析报告

Bug描述

领用出库:选择领用药品后"总库存数量"列数据未显示

数据流分析

  1. 用户点击"添加行" → 新增一行totalQuantity 初始化为空字符串 ''
  2. 用户在"项目"列通过 PopoverList 选择药品 → 触发 selectRow(rowValue, index)
  3. selectRow 设置药品基本信息,然后调用 handleLocationClick(1, rowValue, index)
  4. handleLocationClick 调用 getCount({ itemId, orgLocationId }) 获取库存
  5. getCount 返回 LocationInventoryDto[] 列表,前端通过 pickBestOrgQuantityRow 选最大值
  6. applyFromDto 设置 r.totalQuantity = d.orgQuantity || 0

根因定位

selectRow 函数中第1022-1049行选择药品后

form.purchaseinventoryList[index].unitList = rowValue.unitList[0];

但后端 /app-common/inventory-item 接口返回的 unitList 只设置了 unitCodeminUnitCode没有设置 unitCode_dictTextminUnitCode_dictText

handleLocationClickapplyFromDto第1099-1121行

r.unitCode = r.unitList.minUnitCode;
r.unitCode_dictText = r.unitList.minUnitCode_dictText;  // ← undefined!
if (r.unitCode == r.unitList.minUnitCode) {  // ← 这个条件始终为 true
    r.price = d.price / r.partPercent || '';
    r.price = r.price.toFixed(4);
}

关键问题:r.unitCode 刚被设为 r.unitList.minUnitCode,然后条件 r.unitCode == r.unitList.minUnitCode 始终为 true 导致即使价格很小(如 0.05/1=0.05),也会进入这个分支。

但这不是总库存数量未显示的根本原因。

真正根因:handleLocationClick 函数在调用 getCount 获取库存数据后,applyFromDtor.totalQuantity = d.orgQuantity || 0 的赋值逻辑依赖 d.orgQuantity > 0 的前置判断。

查看前端代码流程:

  • selectRow 设置 totalQuantity: ''(新增行时的默认值)
  • 然后调用 handleLocationClickgetCount → 后端返回数据
  • pickBestOrgQuantityRow 从返回列表中选出 orgQuantity 最大的记录
  • 如果 d && Number(d.orgQuantity ?? 0) > 0 → 调用 applyFromDto → 设置 r.totalQuantity = d.orgQuantity || 0
  • 如果条件不满足(所有记录 orgQuantity 都为 0 或返回空列表)→ applyFromDto 不被调用r.totalQuantity 保持空字符串 ''

进一步分析发现:

  • 如果后端 getCount 返回空列表(该药品在该仓库无库存),d 为 nullapplyFromDto 不会被调用
  • 但如果该药品在仓库确实有库存,问题可能出在前端数据传递上

核心问题在于 unitList 结构不完整: selectRowrowValue.unitList 来自药品列表查询结果,其 unitList 由后端 CommonServiceImpl.getInventoryItemList 构建, 只包含 unitCodeminUnitCode,缺少 unitCode_dictTextminUnitCode_dictText

handleLocationClickapplyFromDto 中,r.unitCoder.unitCode_dictText 的赋值依赖于 unitList 中的字段。 如果 r.unitList 是从 rowValue.unitList[0] 赋值而来(在 selectRow 中),那它应该至少有 unitCodeminUnitCode

但是! 编辑模式(getTransferProductDetails)中,unitList 的构建方式不同:

form.purchaseinventoryList[index].unitList = e.unitList[0]; // 编辑详情时

新增模式(selectRow)中:

form.purchaseinventoryList[index].unitList = rowValue.unitList[0];

两种方式获取的 unitList 结构可能不同。

根本原因: handleLocationClick 中的 getCount API 调用,返回的 LocationInventoryDto 确实包含 orgQuantity。 前端通过 pickBestOrgQuantityRow 选出最大值的记录后,调用 applyFromDto 设置 totalQuantity。 如果药品在仓库有库存但 totalQuantity 仍为空白,说明 applyFromDto 中的 d.orgQuantity 可能为 null/undefined

经检查 selectInventoryItemInfo SQL

SUM(CASE WHEN T1.location_id = #{orgLocationId} THEN T1.quantity ELSE 0 END) AS org_quantity

objLocationId 为 null/空时WHERE 子句为:

AND T1.location_id = #{orgLocationId}

这意味着查询结果中的所有记录都来自 orgLocationId 对应的仓库。 此时 org_quantity 应该等于 SUM(T1.quantity)

如果查询结果为空(该药品在该仓库没有库存记录),则前端 d 为 nullapplyFromDto 不被调用totalQuantity 保持空字符串。

但 Bug 的期望是"应实时检索并填充总库存数量"——如果仓库确实没有该药品的库存,那显示空白是合理的。 但如果仓库有库存却未显示说明前端传递的参数orgLocationId 或 itemId有问题。

最终根因:前端 handleLocationClick 函数中,orgLocationId 的取值可能为空字符串, 导致后端查询时使用空字符串作为 location_id 条件,查不到任何记录。

let orgLocationId = r.sourceLocationId || receiptHeaderForm.headerLocationId || '';

虽然 Bug 步骤中说先选了"西药库",但如果 receiptHeaderForm.headerLocationId 在 selectRow 时已正确设置, r.sourceLocationId 也应该被设置(在 selectRow 第1037行

form.purchaseinventoryList[index].sourceLocationId =
    receiptHeaderForm.headerLocationId || form.purchaseinventoryList[index].sourceLocationId || '';

但这里有一个微妙的时序问题:handleLocationClickgetPharmacyCabinetList().then() 内部被调用, handleLocationClick 是同步执行的,不等待 getPharmacyCabinetList 完成。 这本身不影响 orgLocationId 的取值,因为 orgLocationId 不依赖 getPharmacyCabinetList

修复方案

  1. 确保 applyFromDto 即使在 orgQuantity 为 0 时也能被调用,正确显示"0"而不是空白
  2. 确保 unitList 包含必要的字典文本字段

影响范围

  • 前端文件openhis-ui-vue3/src/views/medicationmanagement/requisitionManagement/requisitionManagement/index.vue
  • 涉及函数:selectRowhandleLocationClick