docs(bug): 诸葛亮分析报告 Bug #758

This commit is contained in:
2026-06-12 15:34:50 +08:00
parent 2921d4535a
commit 93447b0e46

122
MD/bugs/BUG_758_ANALYSIS.md Normal file
View File

@@ -0,0 +1,122 @@
# 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`
- 该异常未被 `DictUtils``DictAspect` 捕获,直接传播为 HTTP 500 错误
**涉及的关键文件:**
1. `core-common/src/main/java/com/core/common/utils/DictUtils.java``getDictCache()` 方法(核心问题)
2. `healthlink-his-common/src/main/java/com/healthlink/his/common/aspectj/DictAspect.java``processDict()` 方法(缺少异常保护)
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 改回 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
```java
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
```java
} 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 文件的异常处理逻辑,纯后端修复,无需前端改动。