5.3 KiB
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.SysDictDatafrom 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 - 该异常未被
DictUtils或DictAspect捕获,直接传播为 HTTP 500 错误
涉及的关键文件:
core-common/src/main/java/com/core/common/utils/DictUtils.java—getDictCache()方法(核心问题)healthlink-his-common/src/main/java/com/healthlink/his/common/aspectj/DictAspect.java—processDict()方法(缺少异常保护)healthlink-his-application/src/main/java/com/healthlink/his/web/doctorstation/controller/DoctorStationDiagnosisController.java— 被 DictAspect 拦截的控制器healthlink-his-application/src/main/java/com/healthlink/his/web/doctorstation/dto/DiagnosisQueryDto.java— 含@Dict注解的 DTO
与历史 Bug 的关联: 此前 commit babd8d0c0 修复了类似问题(Jackson 配置从 ObjectMapper bean 改回 Jackson2ObjectMapperBuilderCustomizer),但 DictUtils.getDictCache() 的 convertValue 仍缺少防御性异常处理。
三、修复方案
修改 1:DictUtils.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;
}
}
修改 2:DictAspect.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: 修复涉及 DictUtils(core-common 模块)和 DictAspect(healthlink-his-common 模块)两个后端 Java 文件的异常处理逻辑,纯后端修复,无需前端改动。
路由决策
- 修复 Agent: guanyu
- 原因: ** 修复涉及
DictUtils(core-common 模块)和DictAspect(healthlink-his-common 模块)两个后端 Java 文件的异常处理逻辑,纯后端修复,无需前端改动。