651 [住院医生站-手术申请] 无法检索出已启用的手术项目(如:“血管闭合切割刀”)

This commit is contained in:
wangjian963
2026-06-15 16:55:17 +08:00
parent 43ab5b4498
commit 9bbf7c6c08
4 changed files with 58 additions and 47 deletions

View File

@@ -2697,21 +2697,6 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
log.info("getSurgeryPage 开始: orgId={}, page={}/{}, searchKey={}", organizationId, pageNo, pageSize, searchKey);
long start = System.currentTimeMillis();
// 无搜索时尝试从 Redis 缓存读取(手术项目变更频率低,适合缓存)
String safeOrgId = organizationId != null ? organizationId.toString() : "";
String cacheKey = "surgery:page:" + safeOrgId + ":" + pageNo + ":" + pageSize;
boolean useCache = (searchKey == null || searchKey.trim().isEmpty());
if (useCache) {
Object cachedObj = redisCache.getCacheObject(cacheKey);
if (cachedObj instanceof com.baomidou.mybatisplus.extension.plugins.pagination.Page) {
log.info("从 Redis 缓存获取手术项目, key: {}, records: {}", cacheKey,
((IPage<?>) cachedObj).getRecords().size());
return (IPage<SurgeryItemDto>) cachedObj;
}
}
// 使用 MyBatis Plus 分页查询
IPage<SurgeryItemDto> result = doctorStationAdviceAppMapper.getSurgeryPage(
new Page<>(pageNo, pageSize),
PublicationStatus.ACTIVE.getValue(),
@@ -2720,12 +2705,6 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
log.info("getSurgeryPage 完成: {}ms, total={}, records={}", System.currentTimeMillis() - start, result.getTotal(), result.getRecords().size());
// 无搜索时将结果写入缓存
if (useCache && result instanceof com.baomidou.mybatisplus.extension.plugins.pagination.Page) {
redisCache.setCacheObject(cacheKey, result, (int) CACHE_EXPIRE_HOURS, java.util.concurrent.TimeUnit.HOURS);
log.info("缓存手术项目, key: {}, 过期时间: {} 小时", cacheKey, CACHE_EXPIRE_HOURS);
}
return result;
}

View File

@@ -45,6 +45,12 @@ public class SurgeryItemDto {
@JsonProperty("unitCode_dictText")
private String unitCodeDictText;
/** 拼音码(前端穿梭框本地搜索用) */
private String pyStr;
/** 业务编号(前端穿梭框本地搜索用) */
private String busNo;
/** 所需标本编码(来自诊疗目录配置,对应字典 specimen_code 的 dictValue */
private String specimenCode;
}

View File

@@ -903,7 +903,9 @@
t2.ID AS charge_item_definition_id,
t2.price AS price,
t1.permitted_unit_code AS unit_code,
COALESCE(sdd.dict_label, t1.permitted_unit_code) AS unit_code_dict_text
COALESCE(sdd.dict_label, t1.permitted_unit_code) AS unit_code_dict_text,
t1.py_str AS py_str,
t1.bus_no AS bus_no
FROM wor_activity_definition t1
LEFT JOIN adm_charge_item_definition t2
ON t2.instance_id = t1.ID
@@ -915,6 +917,7 @@
AND sdd.dict_type = 'unit_code'
AND sdd.status = '0'
WHERE t1.delete_flag = '0'
AND t1.status_enum = #{statusEnum}
AND (t1.category_code = '手术' OR t1.category_code = '24')
<if test="searchKey != null and searchKey != ''">
AND (t1.name ILIKE '%' || #{searchKey} || '%' OR t1.py_str ILIKE '%' || #{searchKey} || '%')
@@ -949,6 +952,7 @@
AND sdd.dict_type = 'unit_code'
AND sdd.status = '0'
WHERE t1.delete_flag = '0'
AND t1.status_enum = #{statusEnum}
AND t1.category_code = #{categoryCode}
<if test="searchKey != null and searchKey != ''">
AND (t1.name ILIKE '%' || #{searchKey} || '%' OR t1.py_str ILIKE '%' || #{searchKey} || '%')

View File

@@ -5,7 +5,20 @@
-->
<template>
<div class="surgery-container">
<div class="search-wrapper">
<el-input
v-model="searchQuery"
placeholder="项目代码/名称/拼音码"
clearable
prefix-icon="Search"
style="width: 480px;"
@input="handleSearch"
@clear="handleClear"
/>
<span class="loaded-count">已加载 {{ applicationList.length }} </span>
</div>
<div
v-loading="loading"
class="transfer-wrapper"
style="min-height: 300px;"
>
@@ -14,10 +27,6 @@
v-model="transferValue"
:data="applicationList"
:titles="['待选择', '已选择']"
:format="leftPanelFormat"
filterable
filter-placeholder="项目代码/名称"
:filter-method="filterMethod"
/>
</div>
<div class="bloodTransfusion-form">
@@ -356,12 +365,6 @@ let surgeryRecordsCache = null; // 原始 API 记录
let surgeryMappedCache = null; // 映射后的 el-transfer 数据
let doctorCache = null; // 医生列表(含默认主刀医生 ID
const transferRef = ref(null);
const dbTotal = ref(0); // 数据库中的手术项目总数
const checkedCount = computed(() => transferValue.value.length);
const leftPanelFormat = computed(() => ({
noChecked: ` 0/${dbTotal.value}`,
hasChecked: ` ${checkedCount.value}/${dbTotal.value}`,
}));
// 递归查找树形科室节点
const findTreeItem = (list, id) => {
if (!list || list.length === 0 || id == null || id === '') return null;
@@ -393,6 +396,23 @@ const loading = ref(false);
const applicationList = ref([]);
const applicationListAll = ref([]);
const allLoading = ref(false);
// 搜索关键字(远程搜索用)
const searchQuery = ref('');
let searchTimer = null;
/** 远程搜索:输入关键字后 300ms 防抖,调后端 API 搜索 */
const handleSearch = () => {
if (searchTimer) clearTimeout(searchTimer);
searchTimer = setTimeout(() => {
getList(searchQuery.value || '');
}, 300);
};
/** 清除搜索:恢复完整缓存列表 */
const handleClear = () => {
searchQuery.value = '';
getList();
};
// 递归查找默认科室
const findTargetDepartment = (selectProjectIds) => {
if (!selectProjectIds || selectProjectIds.length === 0) return '';
@@ -416,13 +436,12 @@ const getList = async (key) => {
if (!key && surgeryRecordsCache && surgeryMappedCache) {
applicationList.value = surgeryMappedCache;
applicationListAll.value = surgeryRecordsCache;
dbTotal.value = surgeryRecordsCache.length;
return;
}
loading.value = true;
return getSurgeryPage({
pageSize: 1000,
keyword: key || undefined,
pageSize: key ? 500 : 100,
searchKey: key || undefined,
})
.then((res) => {
const records = res.data.records;
@@ -440,25 +459,15 @@ const getList = async (key) => {
.catch((e) => {
console.error('手术项目加载失败:', e);
applicationList.value = [];
dbTotal.value = 0;
loading.value = false;
});
};
/**
* el-transfer 内置过滤方法:支持任意字符即时过滤
* 按项目名称/代码进行前端模糊匹配
*/
const filterMethod = (query, item) => {
const q = query.toLowerCase();
const label = (item.label || '').toLowerCase();
const key = String(item.key || '');
return label.includes(q) || key.includes(q);
};
const mapToTransferItem = (item) => ({
key: String(item.adviceDefinitionId),
label: `${item.adviceName} - ${item.unitCode_dictText || item.unitCode || ''}`,
pyStr: item.pyStr || '',
busNo: item.busNo || '',
disabled: false,
});
@@ -727,6 +736,19 @@ defineExpose({ state, submit, fillForm, getLocationInfo, getDiagnosisList, getLi
height: 100%;
width: 100%;
.search-wrapper {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 12px;
.loaded-count {
font-size: 13px;
color: #64748B;
white-space: nowrap;
}
}
.transfer-wrapper {
position: relative;
min-height: 300px;