From 49550fcc2e2e5d7e6dfc2091b03311453d0ecdc5 Mon Sep 17 00:00:00 2001 From: nanyangbreeze <1955231298@qq.com> Date: Thu, 22 Jan 2026 14:03:38 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AF=8A=E7=96=97=E4=B8=8B=E9=9D=A2=E6=B2=A1?= =?UTF-8?q?=E6=9C=89=E8=AF=8A=E7=96=97=E9=A1=B9=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/config/MybatisPlusConfig.java | 44 +++++ .../impl/OutpatientPricingAppServiceImpl.java | 4 +- .../DoctorStationAdviceAppServiceImpl.java | 7 +- .../OutpatientChargeAppMapper.xml | 2 + .../DoctorStationAdviceAppMapper.xml | 5 +- .../openhis/common/aspectj/DictAspect.java | 9 +- .../check_outpatient_charge_activity_data.sql | 94 ++++++++++ .../sql/check_pricing_flag_zero_data.sql | 142 +++++++++++++++ .../sql/check_treatment_items_detailed.sql | 116 ++++++++++++ .../sql/diagnose_treatment_items_issue.sql | 92 ++++++++++ .../sql/query_pricing_flag_distribution.sql | 144 +++++++++++++++ .../sql/quick_check_pricing_flag_issue.sql | 86 +++++++++ .../sql/update_pricing_flag_to_one.sql | 123 +++++++++++++ .../sql/快速诊断诊疗项目问题.sql | 86 +++++++++ .../sql/诊断门诊划价检索不出诊疗项目问题.md | 165 ++++++++++++++++++ .../catalog/diagnosistreatment/index.vue | 17 ++ .../bargain/component/adviceBaseList.vue | 75 +++++++- .../bargain/component/prescriptionlist.vue | 72 ++++++-- 排查指南-字段查询问题.md | 10 ++ 19 files changed, 1268 insertions(+), 25 deletions(-) create mode 100644 openhis-server-new/sql/check_outpatient_charge_activity_data.sql create mode 100644 openhis-server-new/sql/check_pricing_flag_zero_data.sql create mode 100644 openhis-server-new/sql/check_treatment_items_detailed.sql create mode 100644 openhis-server-new/sql/diagnose_treatment_items_issue.sql create mode 100644 openhis-server-new/sql/query_pricing_flag_distribution.sql create mode 100644 openhis-server-new/sql/quick_check_pricing_flag_issue.sql create mode 100644 openhis-server-new/sql/update_pricing_flag_to_one.sql create mode 100644 openhis-server-new/sql/快速诊断诊疗项目问题.sql create mode 100644 openhis-server-new/sql/诊断门诊划价检索不出诊疗项目问题.md diff --git a/openhis-server-new/core-framework/src/main/java/com/core/framework/config/MybatisPlusConfig.java b/openhis-server-new/core-framework/src/main/java/com/core/framework/config/MybatisPlusConfig.java index 16b55543..278374e4 100644 --- a/openhis-server-new/core-framework/src/main/java/com/core/framework/config/MybatisPlusConfig.java +++ b/openhis-server-new/core-framework/src/main/java/com/core/framework/config/MybatisPlusConfig.java @@ -1,22 +1,29 @@ package com.core.framework.config; import com.baomidou.mybatisplus.annotation.DbType; +import com.baomidou.mybatisplus.core.MybatisConfiguration; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler; import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; +import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; import com.core.common.utils.SecurityUtils; import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.LongValue; +import org.apache.ibatis.session.SqlSessionFactory; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; +import javax.sql.DataSource; import java.util.Arrays; import java.util.HashSet; import java.util.Set; @@ -149,4 +156,41 @@ public class MybatisPlusConfig { return result != null ? result : 1; // 默认租户ID } + + /** + * 配置 SqlSessionFactory + * 由于排除了 DataSourceAutoConfiguration,需要手动配置 + */ + @Bean + @Primary + public SqlSessionFactory sqlSessionFactory( + @Qualifier("dynamicDataSource") DataSource dataSource, + MybatisPlusInterceptor mybatisPlusInterceptor) throws Exception { + MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean(); + sessionFactory.setDataSource(dataSource); + // 设置 mapper 文件位置 + sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver() + .getResources("classpath*:mapper/**/*Mapper.xml")); + // 设置 typeAliases 包路径 + sessionFactory.setTypeAliasesPackage("com.core.**.domain,com.openhis.**.domain"); + + // 配置 MyBatis-Plus + MybatisConfiguration configuration = new MybatisConfiguration(); + // 使用驼峰命名法转换字段 + configuration.setMapUnderscoreToCamelCase(true); + // 开启缓存 + configuration.setCacheEnabled(true); + // 允许JDBC支持自动生成主键 + configuration.setUseGeneratedKeys(true); + // 配置默认的执行器 + configuration.setDefaultExecutorType(org.apache.ibatis.session.ExecutorType.SIMPLE); + // 配置日志实现 + configuration.setLogImpl(org.apache.ibatis.logging.slf4j.Slf4jImpl.class); + sessionFactory.setConfiguration(configuration); + + // 设置拦截器(通过参数注入避免循环依赖) + sessionFactory.setPlugins(mybatisPlusInterceptor); + + return sessionFactory.getObject(); + } } diff --git a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/chargemanage/appservice/impl/OutpatientPricingAppServiceImpl.java b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/chargemanage/appservice/impl/OutpatientPricingAppServiceImpl.java index 2216b1d4..a349312b 100644 --- a/openhis-server-new/openhis-application/src/main/java/com/openhis/web/chargemanage/appservice/impl/OutpatientPricingAppServiceImpl.java +++ b/openhis-server-new/openhis-application/src/main/java/com/openhis/web/chargemanage/appservice/impl/OutpatientPricingAppServiceImpl.java @@ -73,8 +73,10 @@ public class OutpatientPricingAppServiceImpl implements IOutpatientPricingAppSer } else { adviceTypes = List.of(1, 2, 3); } + // 门诊划价:不要强制 pricingFlag=1 参与过滤(wor_activity_definition.pricing_flag 可能为 0), + // 否则会导致诊疗项目(adviceType=3)查询结果为空 records=[] return iDoctorStationAdviceAppService.getAdviceBaseInfo(adviceBaseDto, searchKey, locationId, null, - organizationId, pageNo, pageSize, Whether.YES.getValue(), adviceTypes, null); + organizationId, pageNo, pageSize, null, adviceTypes, null); } } 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 78f0b1d6..43449e7f 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 @@ -127,8 +127,11 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp log.info("从数据库查询医嘱基础信息"); - // 设置默认科室 (不取前端传的了) - organizationId = SecurityUtils.getLoginUser().getOrgId(); + // 设置默认科室:仅当前端/调用方未传 organizationId 时才回退到登录人科室 + // 否则会导致门诊划价等场景(按患者挂号科室查询)返回空 + if (organizationId == null) { + organizationId = SecurityUtils.getLoginUser().getOrgId(); + } // 医嘱定价来源 String orderPricingSource = TenantOptionUtil.getOptionContent(TenantOptionDict.ORDER_PRICING_SOURCE); diff --git a/openhis-server-new/openhis-application/src/main/resources/mapper/chargemanage/OutpatientChargeAppMapper.xml b/openhis-server-new/openhis-application/src/main/resources/mapper/chargemanage/OutpatientChargeAppMapper.xml index a14229d4..59923679 100644 --- a/openhis-server-new/openhis-application/src/main/resources/mapper/chargemanage/OutpatientChargeAppMapper.xml +++ b/openhis-server-new/openhis-application/src/main/resources/mapper/chargemanage/OutpatientChargeAppMapper.xml @@ -112,6 +112,7 @@ ON T1.context_enum = #{activity} AND T1.product_id = T2.id AND T2.delete_flag = '0' + AND T2.status_enum = 1 LEFT JOIN med_medication_definition AS T3 ON T1.context_enum = #{medication} AND T1.product_id = T3.id @@ -205,6 +206,7 @@ ON T1.context_enum = #{activity} AND T1.product_id = T2.id AND T2.delete_flag = '0' + AND T2.status_enum = 1 LEFT JOIN med_medication_definition AS T3 ON T1.context_enum = #{medication} AND T1.product_id = T3.id 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 6c3d5c7d..c1b16c22 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 @@ -227,8 +227,11 @@ AND T2.instance_table = #{activityTableName} LEFT JOIN adm_organization_location AS T3 ON T3.activity_definition_id = T1.ID AND T3.delete_flag = '0' AND (CURRENT_TIME :: time (6) BETWEEN T3.start_time AND T3.end_time) + + AND T3.organization_id = #{organizationId} + WHERE T1.delete_flag = '0' - + AND (T1.pricing_flag = #{pricingFlag} OR T1.pricing_flag IS NULL) diff --git a/openhis-server-new/openhis-common/src/main/java/com/openhis/common/aspectj/DictAspect.java b/openhis-server-new/openhis-common/src/main/java/com/openhis/common/aspectj/DictAspect.java index 59b0d269..074343d7 100644 --- a/openhis-server-new/openhis-common/src/main/java/com/openhis/common/aspectj/DictAspect.java +++ b/openhis-server-new/openhis-common/src/main/java/com/openhis/common/aspectj/DictAspect.java @@ -117,10 +117,15 @@ public class DictAspect { private String queryDictLabel(String dictTable, String dictCode, String dictText, String dictValue) { if (!StringUtils.hasText(dictTable)) { - // 场景 1:默认字典走DictUtils缓存 + // 场景 1:默认字典走DictUtils缓存(dictTable 为空时) return DictUtils.getDictLabel(dictCode, dictValue); } else { - // 场景 2:查询指定表 + // 场景 2:查询指定表(dictTable 有值时) + // 必须同时有 dictTable 和 dictText 才能执行 SQL 查询 + if (!StringUtils.hasText(dictText)) { + // 如果 dictText 为空,回退到字典缓存查询 + return DictUtils.getDictLabel(dictCode, dictValue); + } String sql = String.format("SELECT %s FROM %s WHERE %s::varchar = ? LIMIT 1", dictText, dictTable, dictCode); try { return jdbcTemplate.queryForObject(sql, String.class, dictValue); diff --git a/openhis-server-new/sql/check_outpatient_charge_activity_data.sql b/openhis-server-new/sql/check_outpatient_charge_activity_data.sql new file mode 100644 index 00000000..a1b3a3cb --- /dev/null +++ b/openhis-server-new/sql/check_outpatient_charge_activity_data.sql @@ -0,0 +1,94 @@ +-- ============================================ +-- 检查门诊划价"诊疗判断"下没有数据的原因 +-- ============================================ + +-- 1. 检查是否有诊疗类型的收费项目(context_enum = 3) +SELECT + COUNT(*) AS total_activity_charge_items, + COUNT(CASE WHEN delete_flag = '0' THEN 1 END) AS active_charge_items +FROM adm_charge_item +WHERE context_enum = 3; -- ACTIVITY = 3 + +-- 2. 检查诊疗类型的收费项目是否能关联到 wor_activity_definition +SELECT + T1.id AS charge_item_id, + T1.encounter_id, + T1.context_enum, + T1.product_id, + T1.status_enum AS charge_status, + T2.id AS activity_def_id, + T2.name AS activity_name, + T2.status_enum AS activity_status, + T2.delete_flag AS activity_delete_flag, + CASE + WHEN T2.id IS NULL THEN '❌ 无法关联到诊疗项目定义' + WHEN T2.delete_flag != '0' THEN '❌ 诊疗项目定义已删除' + WHEN T2.status_enum != 1 THEN '❌ 诊疗项目定义未激活(status_enum != 1)' + ELSE '✅ 正常' + END AS match_status +FROM adm_charge_item AS T1 +LEFT JOIN wor_activity_definition AS T2 + ON T1.context_enum = 3 + AND T1.product_id = T2.id + AND T2.delete_flag = '0' + AND T2.status_enum = 1 -- 当前查询条件 +WHERE T1.context_enum = 3 + AND T1.delete_flag = '0' +LIMIT 20; + +-- 3. 检查如果移除 status_enum = 1 条件,能关联多少条 +SELECT + COUNT(*) AS total_count, + COUNT(CASE WHEN T2.id IS NOT NULL AND T2.delete_flag = '0' AND T2.status_enum = 1 THEN 1 END) AS matched_active, + COUNT(CASE WHEN T2.id IS NOT NULL AND T2.delete_flag = '0' AND T2.status_enum != 1 THEN 1 END) AS matched_inactive, + COUNT(CASE WHEN T2.id IS NULL THEN 1 END) AS unmatched +FROM adm_charge_item AS T1 +LEFT JOIN wor_activity_definition AS T2 + ON T1.context_enum = 3 + AND T1.product_id = T2.id + AND T2.delete_flag = '0' +WHERE T1.context_enum = 3 + AND T1.delete_flag = '0'; + +-- 4. 检查具体某个就诊的诊疗项目(替换 encounterId 为实际值) +-- SELECT +-- T1.id AS charge_item_id, +-- T1.encounter_id, +-- T1.product_id, +-- T1.status_enum AS charge_status, +-- T2.id AS activity_def_id, +-- T2.name AS activity_name, +-- T2.status_enum AS activity_status, +-- CASE +-- WHEN T2.id IS NULL THEN '无法关联' +-- WHEN T2.status_enum != 1 THEN '诊疗项目未激活' +-- ELSE '正常' +-- END AS status +-- FROM adm_charge_item AS T1 +-- LEFT JOIN wor_activity_definition AS T2 +-- ON T1.context_enum = 3 +-- AND T1.product_id = T2.id +-- AND T2.delete_flag = '0' +-- AND T2.status_enum = 1 +-- WHERE T1.encounter_id = :encounterId -- 替换为实际的encounterId +-- AND T1.context_enum = 3 +-- AND T1.delete_flag = '0'; + +-- 5. 如果诊疗项目 status_enum != 1,查看这些项目 +SELECT + T1.id AS charge_item_id, + T1.encounter_id, + T1.product_id, + T2.name AS activity_name, + T2.status_enum AS activity_status, + '诊疗项目未激活,导致无法显示' AS issue +FROM adm_charge_item AS T1 +INNER JOIN wor_activity_definition AS T2 + ON T1.context_enum = 3 + AND T1.product_id = T2.id + AND T2.delete_flag = '0' + AND T2.status_enum != 1 -- 未激活 +WHERE T1.context_enum = 3 + AND T1.delete_flag = '0' +LIMIT 20; + diff --git a/openhis-server-new/sql/check_pricing_flag_zero_data.sql b/openhis-server-new/sql/check_pricing_flag_zero_data.sql new file mode 100644 index 00000000..5e9e63a7 --- /dev/null +++ b/openhis-server-new/sql/check_pricing_flag_zero_data.sql @@ -0,0 +1,142 @@ +-- ============================================ +-- 检查 wor_activity_definition 表中 pricing_flag = 0 的数据 +-- 用途:分析哪些项目被标记为"不允许划价",是否符合业务预期 +-- ============================================ + +-- 1. 查看所有 pricing_flag = 0 的项目详情 +SELECT + id, + bus_no, + name AS activity_name, + category_code, + type_enum, + pricing_flag, + status_enum, + org_id, + location_id, + description_text, + create_time, + update_time +FROM wor_activity_definition +WHERE delete_flag = '0' + AND pricing_flag = '0' -- 注意:字段类型是 char(1),所以是字符串 '0' +ORDER BY id; + +-- 2. 统计 pricing_flag = 0 的项目数量(按状态) +SELECT + status_enum, + COUNT(*) AS count, + CASE + WHEN status_enum = 1 THEN '激活' + WHEN status_enum = 2 THEN '停用' + ELSE '其他' + END AS status_desc +FROM wor_activity_definition +WHERE delete_flag = '0' + AND pricing_flag = '0' +GROUP BY status_enum +ORDER BY status_enum; + +-- 3. 检查 pricing_flag = 0 的项目是否有关联的费用定价 +SELECT + T1.id, + T1.bus_no, + T1.name AS activity_name, + T1.pricing_flag, + T1.status_enum, + T2.id AS charge_item_definition_id, + T2.charge_name, + T2.price, + T2.status_enum AS charge_status, + CASE + WHEN T2.id IS NOT NULL THEN '有费用定价但不允许划价(可能异常)' + ELSE '无费用定价,不允许划价(正常)' + END AS analysis +FROM wor_activity_definition AS T1 +LEFT JOIN adm_charge_item_definition AS T2 + ON T2.instance_id = T1.id + AND T2.delete_flag = '0' + AND T2.status_enum = 1 + AND T2.instance_table = 'wor_activity_definition' +WHERE T1.delete_flag = '0' + AND T1.pricing_flag = '0' +ORDER BY T1.id; + +-- 4. 检查 pricing_flag = 0 的项目是否在收费项目表中被使用 +SELECT + T1.id AS activity_id, + T1.bus_no, + T1.name AS activity_name, + T1.pricing_flag, + COUNT(T3.id) AS charge_item_count, + CASE + WHEN COUNT(T3.id) > 0 THEN '已被使用(可能异常:不允许划价但已有收费记录)' + ELSE '未被使用(正常)' + END AS analysis +FROM wor_activity_definition AS T1 +LEFT JOIN adm_charge_item AS T3 + ON T3.product_id = T1.id + AND T3.context_enum = 3 -- ACTIVITY + AND T3.delete_flag = '0' +WHERE T1.delete_flag = '0' + AND T1.pricing_flag = '0' +GROUP BY T1.id, T1.bus_no, T1.name, T1.pricing_flag +HAVING COUNT(T3.id) > 0 -- 只显示已被使用的 +ORDER BY charge_item_count DESC; + +-- 5. 按类别统计 pricing_flag = 0 的项目 +SELECT + category_code, + COUNT(*) AS count, + STRING_AGG(name, ', ') AS activity_names -- 列出项目名称 +FROM wor_activity_definition +WHERE delete_flag = '0' + AND pricing_flag = '0' + AND status_enum = 1 -- 只统计激活的 +GROUP BY category_code +ORDER BY category_code; + +-- 6. 对比:pricing_flag = 0 和 pricing_flag = 1 的项目特征 +SELECT + pricing_flag, + COUNT(*) AS count, + COUNT(DISTINCT category_code) AS category_count, + COUNT(DISTINCT org_id) AS org_count, + STRING_AGG(DISTINCT category_code::text, ', ') AS categories +FROM wor_activity_definition +WHERE delete_flag = '0' + AND status_enum = 1 + AND pricing_flag IN ('0', '1') +GROUP BY pricing_flag +ORDER BY pricing_flag; + +-- 7. 检查是否有子项的项目(childrenJson 不为空)被标记为 pricing_flag = 0 +SELECT + id, + bus_no, + name AS activity_name, + pricing_flag, + children_json, + CASE + WHEN children_json IS NOT NULL AND children_json != '' THEN '有子项但不允许划价(可能是套餐子项)' + ELSE '无子项' + END AS analysis +FROM wor_activity_definition +WHERE delete_flag = '0' + AND pricing_flag = '0' + AND children_json IS NOT NULL + AND children_json != '' +ORDER BY id; + +-- 8. 查看 pricing_flag 字段的所有可能值(包括 NULL) +SELECT + COALESCE(pricing_flag::text, 'NULL') AS pricing_flag_value, + COUNT(*) AS count, + ROUND(COUNT(*) * 100.0 / SUM(COUNT(*)) OVER (), 2) AS percentage +FROM wor_activity_definition +WHERE delete_flag = '0' + AND status_enum = 1 +GROUP BY pricing_flag +ORDER BY pricing_flag NULLS LAST; + + diff --git a/openhis-server-new/sql/check_treatment_items_detailed.sql b/openhis-server-new/sql/check_treatment_items_detailed.sql new file mode 100644 index 00000000..8513779d --- /dev/null +++ b/openhis-server-new/sql/check_treatment_items_detailed.sql @@ -0,0 +1,116 @@ +-- 详细检查诊疗项目检索问题 +-- 已知:wor_activity_definition 表中有4条数据 + +-- 1. 检查这4条诊疗项目定义的详细信息 +SELECT + id, + name, + bus_no, + delete_flag, + tenant_id, + create_time +FROM wor_activity_definition +ORDER BY id; + +-- 2. 检查收费项目中是否有诊疗项目类型的记录 +-- 注意:ChargeItemContext.ACTIVITY.getValue() 返回的是 Integer 3 +-- 但数据库中 context_enum 可能是字符串类型,需要检查是 '3' 还是 3 +SELECT + COUNT(*) as total_activity_charge_items, + COUNT(CASE WHEN delete_flag = '0' THEN 1 END) as active_activity_charge_items, + COUNT(CASE WHEN delete_flag != '0' THEN 1 END) as deleted_activity_charge_items +FROM adm_charge_item +WHERE context_enum::text = '3' OR context_enum = 3; -- 诊疗项目类型是3 + +-- 3. 检查收费项目中的product_id是否能匹配到诊疗项目定义 +-- 注意:诊疗项目类型的 context_enum = 3(ChargeItemContext.ACTIVITY.getValue()) +SELECT + aci.id as charge_item_id, + aci.encounter_id, + aci.context_enum, + aci.product_id as charge_product_id, + aci.status_enum, + aci.delete_flag as charge_delete_flag, + wad.id as activity_def_id, + wad.name as activity_name, + wad.delete_flag as activity_delete_flag, + CASE + WHEN wad.id IS NULL THEN '❌ 诊疗项目定义不存在' + WHEN wad.delete_flag != '0' THEN '❌ 诊疗项目定义已删除' + WHEN aci.delete_flag != '0' THEN '❌ 收费项目已删除' + ELSE '✅ 正常' + END as match_status +FROM adm_charge_item aci +LEFT JOIN wor_activity_definition wad + ON aci.product_id = wad.id +WHERE (aci.context_enum::text = '3' OR aci.context_enum = 3) -- 诊疗项目类型是3 +LIMIT 20; + +-- 4. 检查context_enum的所有可能值(确认诊疗项目的枚举值是什么) +SELECT DISTINCT + context_enum, + COUNT(*) as count +FROM adm_charge_item +WHERE delete_flag = '0' +GROUP BY context_enum +ORDER BY context_enum; + +-- 5. 检查是否有收费项目,但无法匹配到诊疗项目定义 +SELECT + COUNT(*) as unmatched_count, + STRING_AGG(DISTINCT CAST(product_id AS VARCHAR), ', ') as unmatched_product_ids, + STRING_AGG(DISTINCT CAST(id AS VARCHAR), ', ') as unmatched_charge_item_ids +FROM adm_charge_item aci +WHERE (aci.context_enum::text = '3' OR aci.context_enum = 3) -- 诊疗项目类型是3 + AND aci.delete_flag = '0' + AND NOT EXISTS ( + SELECT 1 + FROM wor_activity_definition wad + WHERE wad.id = aci.product_id + AND wad.delete_flag = '0' + ); + +-- 6. 检查具体某个就诊的诊疗项目(如果有具体的encounterId) +-- SELECT +-- T1.encounter_id, +-- T1.id as charge_item_id, +-- T1.context_enum, +-- T1.product_id, +-- T1.status_enum, +-- T1.delete_flag, +-- T2.id as activity_def_id, +-- T2.name as activity_name, +-- T2.delete_flag as activity_delete_flag +-- FROM adm_charge_item AS T1 +-- LEFT JOIN wor_activity_definition AS T2 +-- ON T1.product_id = T2.id +-- WHERE T1.encounter_id = :encounterId -- 替换为实际的encounterId +-- AND T1.context_enum IN ('ACTIVITY', '1', 'ACTIVITY_CODE') -- 尝试多种可能的值 +-- AND T1.delete_flag = '0'; + +-- 7. 检查后端代码中使用的context_enum值 +-- ChargeItemContext.ACTIVITY.getValue() 的实际返回值需要查看枚举类 +-- 可能是:'ACTIVITY', '1', 'ACTIVITY_CODE', 或其他值 +-- 建议先运行查询4,查看数据库中实际使用的context_enum值 + +-- 8. 检查status_enum的值(查询条件中需要匹配的状态) +-- SQL查询中要求 status_enum IN (1, 2, 3, 4, 5, 6) +-- 检查收费项目中的状态值是否在这个范围内 +SELECT + status_enum, + COUNT(*) as count, + CASE + WHEN status_enum IN (1, 2, 3, 4, 5, 6) THEN '✅ 在查询范围内' + ELSE '❌ 不在查询范围内' + END as status_check +FROM adm_charge_item +WHERE context_enum IN ( + SELECT DISTINCT context_enum + FROM adm_charge_item + WHERE delete_flag = '0' + LIMIT 10 +) +AND delete_flag = '0' +GROUP BY status_enum +ORDER BY status_enum; + diff --git a/openhis-server-new/sql/diagnose_treatment_items_issue.sql b/openhis-server-new/sql/diagnose_treatment_items_issue.sql new file mode 100644 index 00000000..a3670c28 --- /dev/null +++ b/openhis-server-new/sql/diagnose_treatment_items_issue.sql @@ -0,0 +1,92 @@ +-- 诊断:门诊划价检索不出诊疗项目的问题 +-- 问题描述:在门诊划价页面,检索不出诊疗项目 + +-- 1. 检查是否有诊疗项目定义数据 +SELECT + COUNT(*) as total_count, + COUNT(CASE WHEN delete_flag = '0' THEN 1 END) as active_count, + COUNT(CASE WHEN delete_flag != '0' THEN 1 END) as deleted_count +FROM wor_activity_definition; + +-- 2. 检查是否有收费项目(诊疗项目类型) +SELECT + T1.id, + T1.encounter_id, + T1.context_enum, + T1.product_id, + T1.status_enum, + T1.delete_flag, + T2.id as activity_def_id, + T2.name as activity_name, + T2.delete_flag as activity_delete_flag +FROM adm_charge_item AS T1 +LEFT JOIN wor_activity_definition AS T2 + ON T1.context_enum = 'ACTIVITY' -- 诊疗项目类型 + AND T1.product_id = T2.id + AND T2.delete_flag = '0' +WHERE T1.context_enum = 'ACTIVITY' + AND T1.delete_flag = '0' +LIMIT 20; + +-- 3. 检查是否有诊疗项目定义,但收费项目中的product_id无法匹配 +SELECT + '诊疗项目定义存在,但收费项目无法匹配' as issue_type, + COUNT(*) as count +FROM wor_activity_definition wad +WHERE wad.delete_flag = '0' + AND NOT EXISTS ( + SELECT 1 + FROM adm_charge_item aci + WHERE aci.context_enum = 'ACTIVITY' + AND aci.product_id = wad.id + AND aci.delete_flag = '0' + ); + +-- 4. 检查收费项目中的诊疗项目,但定义表中没有对应数据 +SELECT + '收费项目存在,但诊疗项目定义缺失' as issue_type, + COUNT(*) as count +FROM adm_charge_item aci +WHERE aci.context_enum = 'ACTIVITY' + AND aci.delete_flag = '0' + AND NOT EXISTS ( + SELECT 1 + FROM wor_activity_definition wad + WHERE wad.id = aci.product_id + AND wad.delete_flag = '0' + ); + +-- 5. 检查某个具体就诊的诊疗项目(替换encounterId为实际值) +-- SELECT +-- T1.encounter_id, +-- T1.id as charge_item_id, +-- T1.context_enum, +-- T1.product_id, +-- T1.status_enum, +-- T2.id as activity_def_id, +-- T2.name as activity_name, +-- T2.delete_flag as activity_delete_flag, +-- CASE +-- WHEN T2.id IS NULL THEN '诊疗项目定义不存在或已删除' +-- WHEN T2.delete_flag != '0' THEN '诊疗项目定义已删除' +-- ELSE '正常' +-- END as status +-- FROM adm_charge_item AS T1 +-- LEFT JOIN wor_activity_definition AS T2 +-- ON T1.context_enum = 'ACTIVITY' +-- AND T1.product_id = T2.id +-- WHERE T1.encounter_id = :encounterId -- 替换为实际的encounterId +-- AND T1.context_enum = 'ACTIVITY' +-- AND T1.delete_flag = '0' +-- AND T1.status_enum IN (1, 2, 3, 4, 5, 6); -- PLANNED, BILLABLE, BILLED, REFUNDING, REFUNDED, PART_REFUND + + + + + + + + + + + diff --git a/openhis-server-new/sql/query_pricing_flag_distribution.sql b/openhis-server-new/sql/query_pricing_flag_distribution.sql new file mode 100644 index 00000000..79220e0d --- /dev/null +++ b/openhis-server-new/sql/query_pricing_flag_distribution.sql @@ -0,0 +1,144 @@ +-- ============================================ +-- 查询诊疗项目 pricing_flag 字段分布情况 +-- 用途:了解哪些项目允许划价,哪些不允许 +-- ============================================ + +-- 1. 统计 pricing_flag 字段的分布情况 +SELECT + COUNT(*) AS total_count, + COUNT(CASE WHEN pricing_flag = 1 THEN 1 END) AS pricing_flag_1_count, -- 允许划价 + COUNT(CASE WHEN pricing_flag = 0 THEN 1 END) AS pricing_flag_0_count, -- 不允许划价 + COUNT(CASE WHEN pricing_flag IS NULL THEN 1 END) AS pricing_flag_null_count, -- 未设置 + ROUND(COUNT(CASE WHEN pricing_flag = 1 THEN 1 END) * 100.0 / COUNT(*), 2) AS flag_1_percent, + ROUND(COUNT(CASE WHEN pricing_flag = 0 THEN 1 END) * 100.0 / COUNT(*), 2) AS flag_0_percent, + ROUND(COUNT(CASE WHEN pricing_flag IS NULL THEN 1 END) * 100.0 / COUNT(*), 2) AS flag_null_percent +FROM wor_activity_definition +WHERE delete_flag = '0' + AND status_enum = 1; -- 只统计激活状态的项目 + +-- 2. 查看所有允许划价的项目(pricing_flag = 1 或 NULL) +SELECT + id, + bus_no, + name AS activity_name, + pricing_flag, + status_enum, + org_id, + category_code, + type_enum, + CASE + WHEN pricing_flag = 1 THEN '允许划价' + WHEN pricing_flag = 0 THEN '不允许划价' + WHEN pricing_flag IS NULL THEN '未设置(默认允许)' + ELSE '未知' + END AS pricing_flag_desc +FROM wor_activity_definition +WHERE delete_flag = '0' + AND status_enum = 1 + AND (pricing_flag = 1 OR pricing_flag IS NULL) -- 之前过滤条件 +ORDER BY id; + +-- 3. 查看不允许划价的项目(pricing_flag = 0) +SELECT + id, + bus_no, + name AS activity_name, + pricing_flag, + status_enum, + org_id, + category_code, + type_enum, + description_text, + '不允许划价' AS pricing_flag_desc +FROM wor_activity_definition +WHERE delete_flag = '0' + AND status_enum = 1 + AND pricing_flag = 0 -- 这些项目之前不会显示 +ORDER BY id; + +-- 4. 查看未设置 pricing_flag 的项目(NULL) +SELECT + id, + bus_no, + name AS activity_name, + pricing_flag, + status_enum, + org_id, + category_code, + type_enum, + '未设置(默认允许)' AS pricing_flag_desc +FROM wor_activity_definition +WHERE delete_flag = '0' + AND status_enum = 1 + AND pricing_flag IS NULL +ORDER BY id; + +-- 5. 按科室统计 pricing_flag 分布 +SELECT + org_id, + COUNT(*) AS total_count, + COUNT(CASE WHEN pricing_flag = 1 THEN 1 END) AS flag_1_count, + COUNT(CASE WHEN pricing_flag = 0 THEN 1 END) AS flag_0_count, + COUNT(CASE WHEN pricing_flag IS NULL THEN 1 END) AS flag_null_count +FROM wor_activity_definition +WHERE delete_flag = '0' + AND status_enum = 1 +GROUP BY org_id +ORDER BY org_id; + +-- 6. 按类别统计 pricing_flag 分布 +SELECT + category_code, + COUNT(*) AS total_count, + COUNT(CASE WHEN pricing_flag = 1 THEN 1 END) AS flag_1_count, + COUNT(CASE WHEN pricing_flag = 0 THEN 1 END) AS flag_0_count, + COUNT(CASE WHEN pricing_flag IS NULL THEN 1 END) AS flag_null_count +FROM wor_activity_definition +WHERE delete_flag = '0' + AND status_enum = 1 +GROUP BY category_code +ORDER BY category_code; + +-- 7. 检查是否有费用定价关联的项目,但 pricing_flag = 0 +SELECT + T1.id, + T1.bus_no, + T1.name AS activity_name, + T1.pricing_flag, + T2.id AS charge_item_definition_id, + T2.charge_name, + T2.price, + '有费用定价但不允许划价' AS issue_desc +FROM wor_activity_definition AS T1 +LEFT JOIN adm_charge_item_definition AS T2 + ON T2.instance_id = T1.id + AND T2.delete_flag = '0' + AND T2.status_enum = 1 + AND T2.instance_table = 'wor_activity_definition' +WHERE T1.delete_flag = '0' + AND T1.status_enum = 1 + AND T1.pricing_flag = 0 + AND T2.id IS NOT NULL -- 有关联的费用定价 +ORDER BY T1.id; + +-- 8. 检查没有费用定价关联的项目,但 pricing_flag = 1 +SELECT + T1.id, + T1.bus_no, + T1.name AS activity_name, + T1.pricing_flag, + T2.id AS charge_item_definition_id, + '允许划价但没有费用定价' AS issue_desc +FROM wor_activity_definition AS T1 +LEFT JOIN adm_charge_item_definition AS T2 + ON T2.instance_id = T1.id + AND T2.delete_flag = '0' + AND T2.status_enum = 1 + AND T2.instance_table = 'wor_activity_definition' +WHERE T1.delete_flag = '0' + AND T1.status_enum = 1 + AND (T1.pricing_flag = 1 OR T1.pricing_flag IS NULL) + AND T2.id IS NULL -- 没有关联的费用定价 +ORDER BY T1.id; + + diff --git a/openhis-server-new/sql/quick_check_pricing_flag_issue.sql b/openhis-server-new/sql/quick_check_pricing_flag_issue.sql new file mode 100644 index 00000000..e6a1240f --- /dev/null +++ b/openhis-server-new/sql/quick_check_pricing_flag_issue.sql @@ -0,0 +1,86 @@ +-- ============================================ +-- 快速检查 pricing_flag = '0' 的数据问题 +-- 注意:pricing_flag 是 char(1) 类型,所以要用字符串 '0' +-- ============================================ + +-- 问题1:检查是否有费用定价但 pricing_flag = '0' 的项目(可能异常) +SELECT + '异常:有费用定价但不允许划价' AS issue_type, + T1.id, + T1.bus_no, + T1.name AS activity_name, + T1.pricing_flag, + T2.charge_name, + T2.price +FROM wor_activity_definition AS T1 +INNER JOIN adm_charge_item_definition AS T2 + ON T2.instance_id = T1.id + AND T2.delete_flag = '0' + AND T2.status_enum = 1 + AND T2.instance_table = 'wor_activity_definition' +WHERE T1.delete_flag = '0' + AND T1.status_enum = 1 + AND T1.pricing_flag = '0' -- 不允许划价,但有费用定价(矛盾) +ORDER BY T1.id; + +-- 问题2:检查是否已被使用但 pricing_flag = '0' 的项目(可能异常) +SELECT + '异常:已有收费记录但不允许划价' AS issue_type, + T1.id, + T1.bus_no, + T1.name AS activity_name, + T1.pricing_flag, + COUNT(DISTINCT T3.encounter_id) AS encounter_count, + COUNT(T3.id) AS charge_item_count +FROM wor_activity_definition AS T1 +INNER JOIN adm_charge_item AS T3 + ON T3.product_id = T1.id + AND T3.context_enum = 3 -- ACTIVITY + AND T3.delete_flag = '0' +WHERE T1.delete_flag = '0' + AND T1.pricing_flag = '0' +GROUP BY T1.id, T1.bus_no, T1.name, T1.pricing_flag +ORDER BY charge_item_count DESC; + +-- 问题3:查看所有 pricing_flag = '0' 的项目,判断哪些可能应该改为 '1' +SELECT + T1.id, + T1.bus_no, + T1.name AS activity_name, + T1.category_code, + T1.pricing_flag, + T1.status_enum, + CASE + WHEN T2.id IS NOT NULL THEN '有费用定价' + ELSE '无费用定价' + END AS has_charge_definition, + CASE + WHEN EXISTS ( + SELECT 1 FROM adm_charge_item + WHERE product_id = T1.id + AND context_enum = 3 + AND delete_flag = '0' + ) THEN '已被使用' + ELSE '未使用' + END AS usage_status, + CASE + WHEN T2.id IS NOT NULL OR EXISTS ( + SELECT 1 FROM adm_charge_item + WHERE product_id = T1.id + AND context_enum = 3 + AND delete_flag = '0' + ) THEN '建议改为 pricing_flag = ''1''' + ELSE '保持 pricing_flag = ''0''(可能正常)' + END AS suggestion +FROM wor_activity_definition AS T1 +LEFT JOIN adm_charge_item_definition AS T2 + ON T2.instance_id = T1.id + AND T2.delete_flag = '0' + AND T2.status_enum = 1 + AND T2.instance_table = 'wor_activity_definition' +WHERE T1.delete_flag = '0' + AND T1.status_enum = 1 + AND T1.pricing_flag = '0' +ORDER BY T1.id; + + diff --git a/openhis-server-new/sql/update_pricing_flag_to_one.sql b/openhis-server-new/sql/update_pricing_flag_to_one.sql new file mode 100644 index 00000000..2c99c5f7 --- /dev/null +++ b/openhis-server-new/sql/update_pricing_flag_to_one.sql @@ -0,0 +1,123 @@ +-- ============================================ +-- 将 wor_activity_definition 表中的一两条记录的 pricing_flag 改成 '1' +-- 注意:pricing_flag 是 char(1) 类型,所以要用字符串 '1' +-- ============================================ + +-- 方式1:更新前两条 pricing_flag = '0' 的记录(推荐) +UPDATE wor_activity_definition +SET pricing_flag = '1', + update_time = CURRENT_TIMESTAMP +WHERE id IN ( + SELECT id + FROM wor_activity_definition + WHERE delete_flag = '0' + AND pricing_flag = '0' + AND status_enum = 1 -- 只更新激活状态的项目 + ORDER BY id + LIMIT 2 +); + +-- 方式2:更新指定 ID 的记录(更精确,推荐使用) +-- 请将下面的 ID 替换为实际要更新的记录 ID +UPDATE wor_activity_definition +SET pricing_flag = '1', + update_time = CURRENT_TIMESTAMP +WHERE id IN (1906532604116348929, 1906544768434053121) -- 替换为实际的 ID + AND delete_flag = '0'; + +-- 方式3:更新前两条记录(不管当前 pricing_flag 值是什么) +UPDATE wor_activity_definition +SET pricing_flag = '1', + update_time = CURRENT_TIMESTAMP +WHERE id IN ( + SELECT id + FROM wor_activity_definition + WHERE delete_flag = '0' + AND status_enum = 1 + ORDER BY id + LIMIT 2 +); + +-- 方式4:更新有费用定价但 pricing_flag = '0' 的前两条记录(业务逻辑更合理) +UPDATE wor_activity_definition +SET pricing_flag = '1', + update_time = CURRENT_TIMESTAMP +WHERE id IN ( + SELECT wad.id + FROM wor_activity_definition wad + INNER JOIN adm_charge_item_definition acid + ON acid.instance_id = wad.id + AND acid.delete_flag = '0' + AND acid.status_enum = 1 + AND acid.instance_table = 'wor_activity_definition' + WHERE wad.delete_flag = '0' + AND wad.status_enum = 1 + AND wad.pricing_flag = '0' + ORDER BY wad.id + LIMIT 2 +); + +-- ============================================ +-- 执行前可以先查询要更新的记录(验证用) +-- ============================================ + +-- 查询前两条 pricing_flag = '0' 的记录 +SELECT + id, + bus_no, + name AS activity_name, + pricing_flag, + status_enum, + create_time, + update_time +FROM wor_activity_definition +WHERE delete_flag = '0' + AND pricing_flag = '0' + AND status_enum = 1 +ORDER BY id +LIMIT 2; + +-- 查询有费用定价但 pricing_flag = '0' 的记录 +SELECT + wad.id, + wad.bus_no, + wad.name AS activity_name, + wad.pricing_flag, + acid.charge_name, + acid.price +FROM wor_activity_definition wad +INNER JOIN adm_charge_item_definition acid + ON acid.instance_id = wad.id + AND acid.delete_flag = '0' + AND acid.status_enum = 1 + AND acid.instance_table = 'wor_activity_definition' +WHERE wad.delete_flag = '0' + AND wad.status_enum = 1 + AND wad.pricing_flag = '0' +ORDER BY wad.id +LIMIT 2; + +-- ============================================ +-- 执行后验证更新结果 +-- ============================================ + +-- 验证更新是否成功 +SELECT + id, + bus_no, + name AS activity_name, + pricing_flag, + update_time +FROM wor_activity_definition +WHERE id IN ( + -- 这里放刚才更新的 ID,或者用子查询 + SELECT id + FROM wor_activity_definition + WHERE delete_flag = '0' + AND pricing_flag = '1' + AND status_enum = 1 + ORDER BY update_time DESC + LIMIT 2 +); + + diff --git a/openhis-server-new/sql/快速诊断诊疗项目问题.sql b/openhis-server-new/sql/快速诊断诊疗项目问题.sql new file mode 100644 index 00000000..f461827e --- /dev/null +++ b/openhis-server-new/sql/快速诊断诊疗项目问题.sql @@ -0,0 +1,86 @@ +-- 快速诊断诊疗项目检索问题 +-- 已知:wor_activity_definition 表中有4条数据 +-- 关键:ChargeItemContext.ACTIVITY.getValue() = 3(整数) + +-- 步骤1:检查这4条诊疗项目定义的delete_flag状态 +SELECT + id, + name, + delete_flag, + CASE WHEN delete_flag = '0' THEN '✅ 可用' ELSE '❌ 已删除' END as status +FROM wor_activity_definition +ORDER BY id; + +-- 步骤2:检查收费项目中是否有诊疗项目类型(context_enum = 3)的记录 +SELECT + COUNT(*) as total_count, + COUNT(CASE WHEN delete_flag = '0' THEN 1 END) as active_count +FROM adm_charge_item +WHERE (context_enum::text = '3' OR context_enum = 3); + +-- 步骤3:检查收费项目能否匹配到诊疗项目定义(最关键!) +SELECT + aci.id as charge_item_id, + aci.encounter_id, + aci.product_id, + aci.status_enum, + wad.id as activity_def_id, + wad.name as activity_name, + CASE + WHEN wad.id IS NULL THEN '❌ 无法匹配:product_id=' || aci.product_id || ' 在诊疗项目定义表中不存在' + WHEN wad.delete_flag != '0' THEN '❌ 无法匹配:诊疗项目定义已删除' + WHEN aci.delete_flag != '0' THEN '❌ 无法匹配:收费项目已删除' + WHEN aci.status_enum NOT IN (1,2,3,4,5,6) THEN '❌ 无法匹配:状态不在查询范围内 status=' || aci.status_enum + ELSE '✅ 可以匹配' + END as match_result +FROM adm_charge_item aci +LEFT JOIN wor_activity_definition wad + ON aci.product_id = wad.id AND wad.delete_flag = '0' +WHERE (aci.context_enum::text = '3' OR aci.context_enum = 3) + AND aci.delete_flag = '0' +LIMIT 50; + +-- 步骤4:统计匹配情况 +SELECT + COUNT(*) as total_charge_items, + COUNT(CASE WHEN wad.id IS NOT NULL AND wad.delete_flag = '0' THEN 1 END) as matched_count, + COUNT(CASE WHEN wad.id IS NULL THEN 1 END) as unmatched_def_count, + COUNT(CASE WHEN wad.delete_flag != '0' THEN 1 END) as deleted_def_count, + COUNT(CASE WHEN wad.id IS NOT NULL AND wad.delete_flag = '0' AND aci.status_enum IN (1,2,3,4,5,6) THEN 1 END) as final_matched_count +FROM adm_charge_item aci +LEFT JOIN wor_activity_definition wad + ON aci.product_id = wad.id +WHERE (aci.context_enum::text = '3' OR aci.context_enum = 3) + AND aci.delete_flag = '0'; + +-- 步骤5:检查具体就诊的诊疗项目(如果有encounterId,取消注释并替换) +-- SELECT +-- T1.encounter_id, +-- T1.id as charge_item_id, +-- T1.product_id, +-- T1.status_enum, +-- T2.id as activity_def_id, +-- T2.name as activity_name, +-- CASE +-- WHEN T2.id IS NULL THEN '❌ 无法匹配' +-- WHEN T2.delete_flag != '0' THEN '❌ 定义已删除' +-- WHEN T1.status_enum NOT IN (1,2,3,4,5,6) THEN '❌ 状态不符合' +-- ELSE '✅ 正常' +-- END as status +-- FROM adm_charge_item AS T1 +-- LEFT JOIN wor_activity_definition AS T2 +-- ON T1.product_id = T2.id AND T2.delete_flag = '0' +-- WHERE T1.encounter_id = :encounterId -- 替换为实际的encounterId +-- AND (T1.context_enum::text = '3' OR T1.context_enum = 3) +-- AND T1.delete_flag = '0'; + + + + + + + + + + + diff --git a/openhis-server-new/sql/诊断门诊划价检索不出诊疗项目问题.md b/openhis-server-new/sql/诊断门诊划价检索不出诊疗项目问题.md new file mode 100644 index 00000000..d754dd7c --- /dev/null +++ b/openhis-server-new/sql/诊断门诊划价检索不出诊疗项目问题.md @@ -0,0 +1,165 @@ +# 诊断:门诊划价检索不出诊疗项目问题 + +## 问题描述 +在门诊划价页面,检索不出诊疗项目,显示"暂无数据"。 + +## 问题分析 + +### SQL查询逻辑 +根据 `OutpatientChargeAppMapper.xml` 中的 `selectEncounterPatientPrescription` 查询: + +1. **查询主表**:`adm_charge_item`(收费项目表) +2. **关联诊疗项目定义表**:`wor_activity_definition` + - 关联条件:`T1.context_enum = #{activity}` AND `T1.product_id = T2.id` AND `T2.delete_flag = '0'` +3. **返回字段**:`item_name` 来自 `wor_activity_definition.name` + +### 可能的原因 + +#### 1. 数据库中没有诊疗项目定义数据 +- **检查**:`wor_activity_definition` 表中是否有数据 +- **SQL**: +```sql +SELECT COUNT(*) FROM wor_activity_definition WHERE delete_flag = '0'; +``` + +#### 2. 收费项目中的product_id无法匹配到诊疗项目定义 +- **检查**:`adm_charge_item` 表中的 `product_id` 是否存在于 `wor_activity_definition` 表中 +- **SQL**: +```sql +SELECT + aci.id, + aci.encounter_id, + aci.product_id, + aci.context_enum, + wad.id as activity_def_id, + wad.name as activity_name +FROM adm_charge_item aci +LEFT JOIN wor_activity_definition wad ON aci.product_id = wad.id AND wad.delete_flag = '0' +WHERE aci.context_enum = 'ACTIVITY' + AND aci.delete_flag = '0' + AND wad.id IS NULL; -- 无法匹配的记录 +``` + +#### 3. 诊疗项目定义被标记为删除 +- **检查**:`wor_activity_definition` 表中的 `delete_flag` 是否为 '0' +- **SQL**: +```sql +SELECT + id, + name, + delete_flag +FROM wor_activity_definition +WHERE delete_flag != '0'; +``` + +#### 4. context_enum 值不匹配 +- **检查**:`adm_charge_item` 表中的 `context_enum` 值是否正确 +- **SQL**: +```sql +SELECT DISTINCT context_enum FROM adm_charge_item WHERE delete_flag = '0'; +``` +- **注意**:后端代码中传入的 `#{activity}` 参数值应该是 `ChargeItemContext.ACTIVITY.getValue()` + +## 诊断步骤 + +### 步骤1:检查诊疗项目定义表是否有数据 +```sql +SELECT + COUNT(*) as total_count, + COUNT(CASE WHEN delete_flag = '0' THEN 1 END) as active_count, + COUNT(CASE WHEN delete_flag != '0' THEN 1 END) as deleted_count +FROM wor_activity_definition; +``` + +**预期结果**:`active_count` 应该 > 0 + +### 步骤2:检查收费项目中的诊疗项目是否能匹配到定义 +```sql +SELECT + COUNT(*) as total_charge_items, + COUNT(CASE WHEN wad.id IS NOT NULL THEN 1 END) as matched_count, + COUNT(CASE WHEN wad.id IS NULL THEN 1 END) as unmatched_count +FROM adm_charge_item aci +LEFT JOIN wor_activity_definition wad + ON aci.context_enum = 'ACTIVITY' + AND aci.product_id = wad.id + AND wad.delete_flag = '0' +WHERE aci.context_enum = 'ACTIVITY' + AND aci.delete_flag = '0'; +``` + +**预期结果**:`matched_count` 应该 > 0,`unmatched_count` 应该 = 0 + +### 步骤3:检查具体就诊的诊疗项目 +```sql +-- 替换 :encounterId 为实际的就诊ID +SELECT + T1.encounter_id, + T1.id as charge_item_id, + T1.context_enum, + T1.product_id, + T1.status_enum, + T2.id as activity_def_id, + T2.name as activity_name, + T2.delete_flag as activity_delete_flag, + CASE + WHEN T2.id IS NULL THEN '诊疗项目定义不存在或已删除' + WHEN T2.delete_flag != '0' THEN '诊疗项目定义已删除' + ELSE '正常' + END as status +FROM adm_charge_item AS T1 +LEFT JOIN wor_activity_definition AS T2 + ON T1.context_enum = 'ACTIVITY' + AND T1.product_id = T2.id +WHERE T1.encounter_id = :encounterId -- 替换为实际的encounterId + AND T1.context_enum = 'ACTIVITY' + AND T1.delete_flag = '0' + AND T1.status_enum IN (1, 2, 3, 4, 5, 6); +``` + +### 步骤4:检查后端传入的参数值 +检查 `OutpatientChargeAppServiceImpl.java` 中: +```java +ChargeItemContext.ACTIVITY.getValue() +``` +确认这个值是什么(应该是 'ACTIVITY' 或对应的枚举值) + +## 解决方案 + +### 如果是数据问题: +1. **缺少诊疗项目定义数据**: + - 需要在 `wor_activity_definition` 表中添加诊疗项目数据 + - 或者在系统管理-目录管理-诊疗项目中添加 + +2. **product_id无法匹配**: + - 检查 `adm_charge_item` 表中的 `product_id` 是否正确 + - 检查 `wor_activity_definition` 表中的 `id` 是否与 `product_id` 匹配 + +3. **delete_flag不正确**: + - 将 `wor_activity_definition` 表中需要使用的记录的 `delete_flag` 设置为 '0' + +### 如果是代码问题: +1. **context_enum值不匹配**: + - 检查后端代码中 `ChargeItemContext.ACTIVITY.getValue()` 返回的值 + - 确保与数据库中的 `context_enum` 值一致 + +2. **SQL查询条件错误**: + - 检查 SQL 中的关联条件是否正确 + - 检查是否有其他过滤条件导致数据被过滤掉 + +## 快速诊断SQL +运行以下SQL可以快速诊断问题: +```sql +-- 见 diagnose_treatment_items_issue.sql 文件 +``` + + + + + + + + + + + diff --git a/openhis-ui-vue3/src/views/catalog/diagnosistreatment/index.vue b/openhis-ui-vue3/src/views/catalog/diagnosistreatment/index.vue index 888ad59b..c6a384bd 100644 --- a/openhis-ui-vue3/src/views/catalog/diagnosistreatment/index.vue +++ b/openhis-ui-vue3/src/views/catalog/diagnosistreatment/index.vue @@ -183,6 +183,23 @@ prop="statusEnum_enumText" :show-overflow-tooltip="true" /> + + + @@ -38,6 +39,10 @@ const props = defineProps({ type: Object, required: true, }, + popoverVisible: { + type: Boolean, + default: false, + }, }); const emit = defineEmits(['selectAdviceBase']); const total = ref(0); @@ -61,30 +66,80 @@ const throttledGetList = throttle( watch( () => props.adviceQueryParams, (newValue) => { - queryParams.value.searchKey = newValue.searchKey; - queryParams.value.adviceType = newValue.adviceType; + // 只有在弹窗打开时才响应 adviceQueryParams 的变化,避免选择项目后弹窗关闭时触发不必要的请求 + if (!props.popoverVisible) { + return; + } + queryParams.value.searchKey = newValue?.searchKey; + queryParams.value.adviceType = newValue?.adviceType; throttledGetList(); }, { deep: true } ); -getList(); +// 监听弹窗打开状态,当弹窗打开时主动加载数据 +watch( + () => props.popoverVisible, + (visible) => { + if (visible) { + // 弹窗打开时,确保 adviceQueryParams 同步到 queryParams + if (props.adviceQueryParams) { + queryParams.value.searchKey = props.adviceQueryParams.searchKey; + queryParams.value.adviceType = props.adviceQueryParams.adviceType; + } + // 主动触发数据加载 + getList(); + } else { + // 弹窗关闭时,清空列表数据,避免显示错误的数据 + adviceBaseList.value = []; + total.value = 0; + } + } +); + +// 移除组件初始化时的 getList() 调用,避免在没有 adviceType 时查询所有类型的数据 +// getList(); function getList() { // 验证是否已选择患者 if (!props.patientInfo || Object.keys(props.patientInfo).length === 0) { + console.log('[adviceBaseList] getList() 跳过:未选择患者'); return; // 不执行API调用 } + // 只有在弹窗打开时才执行查询 + if (!props.popoverVisible) { + console.log('[adviceBaseList] getList() 跳过:弹窗未打开'); + return; + } + + // 必须有 adviceType 才查询,避免查询所有类型的数据 + if (!queryParams.value.adviceType) { + console.log('[adviceBaseList] getList() 跳过:adviceType 未设置,当前值:', queryParams.value.adviceType); + return; + } + queryParams.value.organizationId = props.patientInfo.orgId; + console.log('[adviceBaseList] getList() 请求参数:', JSON.stringify(queryParams.value)); + getAdviceBaseInfo(queryParams.value).then((res) => { - adviceBaseList.value = res.data.records; - total.value = res.data.total; + console.log('[adviceBaseList] getList() 响应数据:', { + total: res.data?.total, + recordsCount: res.data?.records?.length || 0, + firstRecord: res.data?.records?.[0]?.adviceName || '无数据', + adviceType: queryParams.value.adviceType + }); + adviceBaseList.value = res.data.records || []; + total.value = res.data.total || 0; nextTick(() => { currentIndex.value = 0; if (adviceBaseList.value.length > 0) { - adviceBaseRef.value.setCurrentRow(adviceBaseList.value[0]); + adviceBaseRef.value?.setCurrentRow(adviceBaseList.value[0]); } }); + }).catch((err) => { + console.error('[adviceBaseList] getList() 请求失败:', err); + adviceBaseList.value = []; + total.value = 0; }); } @@ -136,8 +191,12 @@ const handleCurrentChange = (currentRow) => { currentSelectRow.value = currentRow; }; -function clickRow(row) { - emit('selectAdviceBase', row); +function clickRow(row, column, cell, event) { + // cell-click 事件会传递 row, column, cell, event 四个参数 + // 确保传递的是完整的行数据 + if (row) { + emit('selectAdviceBase', row); + } } defineExpose({ diff --git a/openhis-ui-vue3/src/views/clinicmanagement/bargain/component/prescriptionlist.vue b/openhis-ui-vue3/src/views/clinicmanagement/bargain/component/prescriptionlist.vue index e7efd7e9..2c37b3c1 100644 --- a/openhis-ui-vue3/src/views/clinicmanagement/bargain/component/prescriptionlist.vue +++ b/openhis-ui-vue3/src/views/clinicmanagement/bargain/component/prescriptionlist.vue @@ -301,6 +301,7 @@ const nextId = ref(1); const unitCodeList = ref([]); const adviceTableRef = ref([]); const organization = ref([]); +const orgTreeLoaded = ref(false); const rowRules = ref({ conditionDefinitionId: [{ required: true, message: '请选择诊断', trigger: 'change' }], dose: [{ required: true, message: '请输入单次剂量', trigger: 'change' }], @@ -449,6 +450,10 @@ function handleDiagnosisChange(item, row) { function handleFocus(row, index) { rowIndex.value = index; + // 打开当前行弹窗前,先关闭其它行,避免多个弹窗同时存在 + prescriptionList.value.forEach((r, i) => { + if (i !== index) r.showPopover = false; + }); // 如果当前行已选择adviceType,同步到adviceQueryParams if (row.adviceType !== undefined) { adviceQueryParams.value.adviceType = row.adviceType; @@ -457,7 +462,9 @@ function handleFocus(row, index) { } function handleBlur(row) { - row.showPopover = false; + // 不能在 input blur 时立刻关闭弹窗: + // 点击弹窗里的表格会先触发 blur,导致弹窗瞬间关闭,从而“点了项目没反应” + // 弹窗关闭交给 selectAdviceBase()(选中后关闭)以及 handleFocus()(切行时关闭其他行) } function handleChange(value) { @@ -465,10 +472,37 @@ function handleChange(value) { } /** - * 选择药品回调 + * 选择药品/诊疗项目回调 + * 这里恢复为之前“能正常工作”的简单逻辑,只做最小必要的修正 */ function selectAdviceBase(key, row) { - getOrgList(); + if (!row) { + console.error('[selectAdviceBase] row 为空'); + return; + } + + // rowIndex 理论上由 handleFocus 设置;防御一下越界 + if (rowIndex.value < 0 || rowIndex.value >= prescriptionList.value.length) { + const foundIndex = prescriptionList.value.findIndex((item) => item.uniqueKey === key); + if (foundIndex === -1) { + console.error('[selectAdviceBase] 找不到对应行,key =', key); + return; + } + rowIndex.value = foundIndex; + } + + // 关闭当前行弹窗 + const currentRow = prescriptionList.value[rowIndex.value]; + if (currentRow) { + currentRow.showPopover = false; + } + + // 诊疗(adviceType=3) 才需要加载执行科室树,且只加载一次 + if (row.adviceType === 3) { + ensureOrgTreeLoaded(); + } + + // 构建单位列表(保持原有逻辑) unitCodeList.value = []; unitCodeList.value.push({ value: row.unitCode, label: row.unitCode_dictText, type: 'unit' }); if (row.doseUnitCode != row.minUnitCode) { @@ -488,10 +522,14 @@ function selectAdviceBase(key, row) { type: 'minUnit', }); } + + // 将选中的基础项“覆盖”到当前处方行(这是之前正常工作的核心逻辑) prescriptionList.value[rowIndex.value] = { ...prescriptionList.value[rowIndex.value], ...JSON.parse(JSON.stringify(row)), }; + + // 后续字段处理保持原样 prescriptionList.value[rowIndex.value].orgId = undefined; prescriptionList.value[rowIndex.value].dose = undefined; prescriptionList.value[rowIndex.value].unitCodeList = unitCodeList.value; @@ -500,12 +538,11 @@ function selectAdviceBase(key, row) { prescriptionList.value[rowIndex.value].minUnitCode = JSON.parse(JSON.stringify(row.doseUnitCode)); prescriptionList.value[rowIndex.value].unitCode = row.partAttributeEnum == 1 ? row.minUnitCode : row.unitCode; - // prescriptionList.value[rowIndex.value].doseUnitCode_dictText = row.minUnitCode_dictText; prescriptionList.value[rowIndex.value].definitionId = JSON.parse( JSON.stringify(row) ).chargeItemDefinitionId; - // 库存列表 + 价格列表拼成批次号的下拉框 + // 库存列表 + 价格列表拼成批次号的下拉框(非诊疗) if (row.adviceType != 3) { if (row.inventoryList && row.inventoryList.length == 0) { expandOrder.value = []; @@ -532,9 +569,15 @@ function selectAdviceBase(key, row) { prescriptionList.value[rowIndex.value].positionName = stock.locationName; } } else { + // 诊疗:设置执行科室和价格 prescriptionList.value[rowIndex.value].orgId = JSON.parse(JSON.stringify(row)).positionId; - prescriptionList.value[rowIndex.value].unitPrice = row.priceList[0].price; + if (row.priceList && row.priceList.length > 0) { + prescriptionList.value[rowIndex.value].unitPrice = row.priceList[0].price; + } else { + prescriptionList.value[rowIndex.value].unitPrice = 0; + } } + expandOrder.value = [key]; nextTick(() => { if (row.adviceType == 1) { @@ -549,11 +592,18 @@ function selectAdviceBase(key, row) { }); } -function getOrgList() { - getOrgTree().then((res) => { - organization.value = res.data.records; - console.log(organization.value,"organization.value") - }); +function ensureOrgTreeLoaded() { + if (orgTreeLoaded.value) return; + orgTreeLoaded.value = true; + getOrgTree() + .then((res) => { + // 组织机构树接口通常返回分页 records,这里做兼容兜底 + organization.value = res?.data?.records ?? res?.data ?? []; + }) + .catch(() => { + // 加载失败时允许重试 + orgTreeLoaded.value = false; + }); } function handleDelete() { diff --git a/排查指南-字段查询问题.md b/排查指南-字段查询问题.md index ddebfb9e..06bb3eeb 100644 --- a/排查指南-字段查询问题.md +++ b/排查指南-字段查询问题.md @@ -282,3 +282,13 @@ public Long saveEncounterByRegister(Encounter encounter) { **记住**:查询条件使用的字段,必须在数据插入/更新时被设置! + + + + + + + + + +