Files
his/MD/bugs/BUG_758_ANALYSIS.md

5.3 KiB
Raw Blame History

Bug #758 诸葛亮分析报告

文档类型: Bug分析 分析时间: 2026-06-12 15:34:50 分析模型: mimo-v2.5 (LLM深度分析)


基本信息

  • Bug #: 758
  • 标题: 【门诊医生工作站-检验】检验信息下的新增的字段按钮点击会出现报错Cannot deserialize value of type com.core.common.core.domain.entity.SysDictData from Array value
  • 模块: 门诊医生工作站
  • 提出人: 王栩坤

I now have a complete understanding of the root cause. Let me provide the analysis.


一、Bug 理解

用户在门诊医生工作站的"检验信息"区域点击"新增"按钮时,页面弹出 Jackson 反序列化错误 Cannot deserialize value of type SysDictData from Array value,无法正常新增检验信息。期望点击新增后能正常清空表单、准备录入新检验申请单。

二、根因分析

错误触发链路:

用户点击"新增" 
→ handleNewApplication() 
→ resetForm() 
→ getEncounterDiagnosis(encounterId) [API 调用]
→ 后端返回 R<List<DiagnosisQueryDto>>
→ DictAspect 拦截 @GetMapping 响应
→ processDict() 发现 DiagnosisQueryDto.medTypeCode 有 @Dict(dictCode = "med_type")
→ DictUtils.getDictLabel("med_type", value)
→ DictUtils.getDictCache("med_type")
→ mapper.convertValue(cached, TypeReference<List<SysDictData>>) ← 💥 这里抛异常
→ DictAspect 无 try-catch异常直接传播到前端

根因:DictUtils.getDictCache() 缺少异常处理

  • 文件:core-common/src/main/java/com/core/common/utils/DictUtils.java:38-62
  • Redis 中的字典缓存数据结构异常(可能是旧 Fastjson 序列化格式遗留、嵌套数组等)
  • Jackson ObjectMapper.convertValue() 无法将异常结构转为 List<SysDictData>,抛出 JsonMappingException
  • 该异常未被 DictUtilsDictAspect 捕获,直接传播为 HTTP 500 错误

涉及的关键文件:

  1. core-common/src/main/java/com/core/common/utils/DictUtils.javagetDictCache() 方法(核心问题)
  2. healthlink-his-common/src/main/java/com/healthlink/his/common/aspectj/DictAspect.javaprocessDict() 方法(缺少异常保护)
  3. healthlink-his-application/src/main/java/com/healthlink/his/web/doctorstation/controller/DoctorStationDiagnosisController.java — 被 DictAspect 拦截的控制器
  4. healthlink-his-application/src/main/java/com/healthlink/his/web/doctorstation/dto/DiagnosisQueryDto.java — 含 @Dict 注解的 DTO

与历史 Bug 的关联: 此前 commit babd8d0c0 修复了类似问题Jackson 配置从 ObjectMapper bean 改回 Jackson2ObjectMapperBuilderCustomizerDictUtils.getDictCache()convertValue 仍缺少防御性异常处理。

三、修复方案

修改 1DictUtils.getDictCache() 添加 try-catch 防御(核心修复)

文件:core-common/src/main/java/com/core/common/utils/DictUtils.java

getDictCache() 方法中,为 mapper.convertValue() 添加 try-catch失败时清理损坏缓存并返回 null

public static List<SysDictData> getDictCache(String key) {
    Object cached = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key));
    if (StringUtils.isNull(cached)) {
        return null;
    }
    if (cached instanceof List && ((List<?>) cached).stream().allMatch(e -> e instanceof SysDictData)) {
        @SuppressWarnings("unchecked")
        List<SysDictData> result = (List<SysDictData>) cached;
        return result;
    }
    try {
        com.fasterxml.jackson.core.type.TypeReference<List<SysDictData>> typeRef =
                new com.fasterxml.jackson.core.type.TypeReference<List<SysDictData>>() {};
        ObjectMapper mapper = new ObjectMapper()
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        if (cached instanceof JsonNode jsonNode) {
            return mapper.convertValue(jsonNode, typeRef);
        }
        return mapper.convertValue(cached, typeRef);
    } catch (Exception e) {
        // 缓存数据格式异常,清理损坏缓存,下次重新加载
        org.slf4j.LoggerFactory.getLogger(DictUtils.class)
            .warn("字典缓存转换失败(key={}),已清理: {}", key, e.getMessage());
        removeDictCache(key);
        return null;
    }
}

修改 2DictAspect.processDict() 添加 try-catch 防御(防止字典翻译失败影响 API 响应)

文件:healthlink-his-common/src/main/java/com/healthlink/his/common/aspectj/DictAspect.java

processDict() 方法中,对字典查询部分包裹 try-catch

} else if (field.isAnnotationPresent(Dict.class)) {
    try {
        // ... 原有字典翻译逻辑 ...
    } catch (Exception e) {
        log.debug("字段 {} 字典翻译失败,跳过: {}", field.getName(), e.getMessage());
    }
}

四、路由决策

FIXER: guanyu后端开发

REASON: 修复涉及 DictUtilscore-common 模块)和 DictAspecthealthlink-his-common 模块)两个后端 Java 文件的异常处理逻辑,纯后端修复,无需前端改动。


路由决策

  • 修复 Agent: guanyu
  • 原因: ** 修复涉及 DictUtilscore-common 模块)和 DictAspecthealthlink-his-common 模块)两个后端 Java 文件的异常处理逻辑,纯后端修复,无需前端改动。