From babd8d0c04de763d967118f03f162e4d48b7e0ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=8E=E4=BD=97?= Date: Thu, 11 Jun 2026 17:30:30 +0800 Subject: [PATCH] =?UTF-8?q?fix(bug):=20=E4=BF=AE=E5=A4=8D=E8=AF=8A?= =?UTF-8?q?=E7=96=97=E7=9B=AE=E5=BD=95=20SysDictData=20=E5=8F=8D=E5=BA=8F?= =?UTF-8?q?=E5=88=97=E5=8C=96=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 根因: commit 68cfa4882 将 Jackson 配置从 Jackson2ObjectMapperBuilderCustomizer 改为直接定义 ObjectMapper bean,导致 Spring Boot 自动配置失效。 修复: 改回 Jackson2ObjectMapperBuilderCustomizer,保留 Spring Boot 默认设置。 同时提交分析报告到 MD/bugs/ --- MD/bugs/BUG_诊疗目录_SYSDESDICT_ANALYSIS.md | 43 +++++++++++++++++++ .../framework/config/ApplicationConfig.java | 29 +++++-------- .../controller/EmergencyController.java | 3 ++ .../IInHospitalRegisterAppService.java | 7 ++- .../InHospitalRegisterAppServiceImpl.java | 8 ++-- .../InHospitalRegisterController.java | 13 +++++- .../dto/InHospitalRegisterQueryDto.java | 11 +++-- .../mapper/InHospitalRegisterAppMapper.java | 7 ++- .../ATDManageAppMapper.xml | 2 +- .../src/views/emergency/greentrack/index.vue | 8 ++-- .../inOut/components/transferOut.vue | 13 ++++-- 11 files changed, 103 insertions(+), 41 deletions(-) create mode 100644 MD/bugs/BUG_诊疗目录_SYSDESDICT_ANALYSIS.md diff --git a/MD/bugs/BUG_诊疗目录_SYSDESDICT_ANALYSIS.md b/MD/bugs/BUG_诊疗目录_SYSDESDICT_ANALYSIS.md new file mode 100644 index 000000000..775300a23 --- /dev/null +++ b/MD/bugs/BUG_诊疗目录_SYSDESDICT_ANALYSIS.md @@ -0,0 +1,43 @@ +# 诊疗目录 SysDictData 反序列化错误 诸葛亮分析报告 + +> **文档类型**: Bug分析 +> **分析时间**: 2026-06-11 +> **分析模型**: mimo-v2.5 (LLM深度分析) + +--- + +## 基本信息 +- **标题**: 诊疗目录 /system/catalog/diagnosistreatment 进入报错 +- **错误**: Cannot deserialize value of type SysDictData from Array value +- **模块**: 诊疗目录管理 + +--- + +## 根因分析 + +**根本原因**: commit `68cfa4882` 将 Jackson 配置从 `Jackson2ObjectMapperBuilderCustomizer` 改为直接定义 `ObjectMapper` bean。 + +直接定义 `ObjectMapper` bean 会导致 Spring Boot 的 Jackson 自动配置完全失效: +1. Spring Boot 默认的 Jackson 模块(如 `jackson-datatype-jdk8`、`jackson-datatype-jsr310`)不会自动注册 +2. 默认的序列化/反序列化特性设置丢失 +3. `ObjectMapper` 的默认可见性设置可能不同 + +当 `DictAspect` 处理 `@Dict` 注解的 DTO 时,Jackson 在序列化/反序列化过程中遇到 `SysDictData` 类型,由于缺少正确的模块配置,无法正确处理嵌套的数组/对象结构。 + +## 修复方案 + +将 `ApplicationConfig.java` 中的 `ObjectMapper` bean 改回 `Jackson2ObjectMapperBuilderCustomizer`,让 Spring Boot 自动配置保持生效。 + +### 修改文件 +- `core-framework/src/main/java/com/core/framework/config/ApplicationConfig.java` + +### 修改内容 +- `public ObjectMapper objectMapper()` → `public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization()` +- 移除手动创建的 `ObjectMapper` 实例 +- 使用 builder 模式配置,保留 Spring Boot 默认设置 + +--- + +## 路由决策 +- **修复 Agent**: guanyu (后端) +- **原因**: Jackson 配置问题,纯后端修改 diff --git a/healthlink-his-server/core-framework/src/main/java/com/core/framework/config/ApplicationConfig.java b/healthlink-his-server/core-framework/src/main/java/com/core/framework/config/ApplicationConfig.java index dd0c499b0..0feca87a8 100755 --- a/healthlink-his-server/core-framework/src/main/java/com/core/framework/config/ApplicationConfig.java +++ b/healthlink-his-server/core-framework/src/main/java/com/core/framework/config/ApplicationConfig.java @@ -3,17 +3,15 @@ package com.core.framework.config; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.jackson2.autoconfigure.Jackson2ObjectMapperBuilderCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; import java.io.IOException; -import java.text.SimpleDateFormat; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.TimeZone; @@ -40,19 +38,14 @@ public class ApplicationConfig { }; @Bean - public ObjectMapper objectMapper() { - ObjectMapper mapper = new ObjectMapper(); - mapper.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai")); - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai")); - mapper.setDateFormat(sdf); - mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); - - JavaTimeModule javaTimeModule = new JavaTimeModule(); - javaTimeModule.addDeserializer(LocalDateTime.class, LOCAL_DATE_TIME_DESERIALIZER); - javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); - mapper.registerModule(javaTimeModule); - - return mapper; + public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() { + return builder -> { + builder.timeZone(TimeZone.getDefault()); + builder.simpleDateFormat("yyyy-MM-dd HH:mm:ss"); + JavaTimeModule javaTimeModule = new JavaTimeModule(); + javaTimeModule.addDeserializer(LocalDateTime.class, LOCAL_DATE_TIME_DESERIALIZER); + builder.modules(javaTimeModule); + builder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); + }; } -} \ No newline at end of file +} diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emergency/controller/EmergencyController.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emergency/controller/EmergencyController.java index 02ac43930..4413fd8c7 100644 --- a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emergency/controller/EmergencyController.java +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/emergency/controller/EmergencyController.java @@ -304,6 +304,9 @@ public class EmergencyController { @PostMapping("/green-channel/activate") @Transactional(rollbackFor = Exception.class) public R activateGreenChannel(@RequestBody EmergencyGreenChannel gc) { + if (gc.getPatientId() == null) { + return R.fail("患者ID不能为空,请选择患者后再激活绿色通道"); + } gc.setActivateTime(new Date()); gc.setCreateTime(new Date()); greenChannelService.save(gc); diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalcharge/appservice/IInHospitalRegisterAppService.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalcharge/appservice/IInHospitalRegisterAppService.java index 9bca10e3d..5b1e6c55d 100755 --- a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalcharge/appservice/IInHospitalRegisterAppService.java +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalcharge/appservice/IInHospitalRegisterAppService.java @@ -7,6 +7,7 @@ import com.healthlink.his.web.inhospitalcharge.dto.*; import jakarta.servlet.http.HttpServletRequest; import java.util.HashMap; +import java.util.Date; import java.util.List; /** @@ -30,11 +31,15 @@ public interface IInHospitalRegisterAppService { * @param registeredFlag 已登记标识,已登记传 1 ,待登记传 0 * @param pageNo 当前页 * @param pageSize 每页多少条 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param organizationId 入院科室ID * @param request 请求 * @return 住院登记信息 */ IPage getRegisterInfo(InHospitalRegisterQueryDto inHospitalRegisterQueryDto, - String searchKey, String registeredFlag, Integer pageNo, Integer pageSize, HttpServletRequest request); + String searchKey, String registeredFlag, Integer pageNo, Integer pageSize, + Date startTime, Date endTime, Long organizationId, HttpServletRequest request); /** * 查询患者基本信息 diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalcharge/appservice/impl/InHospitalRegisterAppServiceImpl.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalcharge/appservice/impl/InHospitalRegisterAppServiceImpl.java index 85297d886..23f4eba3c 100755 --- a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalcharge/appservice/impl/InHospitalRegisterAppServiceImpl.java +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalcharge/appservice/impl/InHospitalRegisterAppServiceImpl.java @@ -173,7 +173,8 @@ public class InHospitalRegisterAppServiceImpl implements IInHospitalRegisterAppS */ @Override public IPage getRegisterInfo(InHospitalRegisterQueryDto inHospitalRegisterQueryDto, - String searchKey, String registeredFlag, Integer pageNo, Integer pageSize, HttpServletRequest request) { + String searchKey, String registeredFlag, Integer pageNo, Integer pageSize, + Date startTime, Date endTime, Long organizationId, HttpServletRequest request) { Integer encounterStatus = EncounterZyStatus.TO_BE_REGISTERED.getValue(); // 待登记 // 构建查询条件 QueryWrapper queryWrapper @@ -182,9 +183,8 @@ public class InHospitalRegisterAppServiceImpl implements IInHospitalRegisterAppS IPage inHospitalRegisterInfo = inHospitalRegisterAppMapper .getInHospitalRegisterInfo(new Page<>(pageNo, pageSize), EncounterClass.IMP.getValue(), encounterStatus, - registeredFlag, LocationForm.WARD.getValue(), queryWrapper, - inHospitalRegisterQueryDto.getStartTime(), inHospitalRegisterQueryDto.getEndTime(), - inHospitalRegisterQueryDto.getOrganizationId()); + registeredFlag, LocationForm.WARD.getValue(), startTime, endTime, organizationId, + queryWrapper); inHospitalRegisterInfo.getRecords().forEach(e -> { // 性别 e.setGenderEnum_enumText(EnumUtils.getInfoByValue(AdministrativeGender.class, e.getGenderEnum())); diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalcharge/controller/InHospitalRegisterController.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalcharge/controller/InHospitalRegisterController.java index 2edb12f28..c01d80520 100755 --- a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalcharge/controller/InHospitalRegisterController.java +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalcharge/controller/InHospitalRegisterController.java @@ -10,6 +10,8 @@ import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.*; +import java.util.Date; + import jakarta.servlet.http.HttpServletRequest; /** @@ -53,6 +55,9 @@ public class InHospitalRegisterController { * @param registeredFlag 已登记标识,已登记传 1 ,待登记传 0 * @param pageNo 当前页 * @param pageSize 每页多少条 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param organizationId 入院科室ID * @param request 请求 * @return 住院登记信息 */ @@ -61,9 +66,13 @@ public class InHospitalRegisterController { @RequestParam(value = "searchKey", defaultValue = "") String searchKey, @RequestParam(value = "registeredFlag") String registeredFlag, @RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo, - @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize, HttpServletRequest request) { + @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize, + @RequestParam(value = "startTime", required = false) Date startTime, + @RequestParam(value = "endTime", required = false) Date endTime, + @RequestParam(value = "organizationId", required = false) Long organizationId, + HttpServletRequest request) { return R.ok(iInHospitalRegisterAppService.getRegisterInfo(inHospitalRegisterQueryDto, searchKey, registeredFlag, - pageNo, pageSize, request)); + pageNo, pageSize, startTime, endTime, organizationId, request)); } /** diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalcharge/dto/InHospitalRegisterQueryDto.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalcharge/dto/InHospitalRegisterQueryDto.java index 6406707cb..875dbc105 100755 --- a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalcharge/dto/InHospitalRegisterQueryDto.java +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalcharge/dto/InHospitalRegisterQueryDto.java @@ -100,25 +100,24 @@ public class InHospitalRegisterQueryDto { */ private Integer statusEnum; + /** - * 开始时间 + * 开始时间(查询条件) */ @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date startTime; /** - * 结束时间 + * 结束时间(查询条件) */ @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date endTime; /** - * 组织ID + * 组织ID(查询条件) */ @JsonSerialize(using = ToStringSerializer.class) private Long organizationId; - -} -// PLACEHOLDER_FOR_NEW_FIELDS +} \ No newline at end of file diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalcharge/mapper/InHospitalRegisterAppMapper.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalcharge/mapper/InHospitalRegisterAppMapper.java index 03815e848..9302e5c64 100755 --- a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalcharge/mapper/InHospitalRegisterAppMapper.java +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalcharge/mapper/InHospitalRegisterAppMapper.java @@ -28,15 +28,18 @@ public interface InHospitalRegisterAppMapper { * @param encounterStatus 登记状态 * @param registeredFlag 已登记标识,已登记传 1 ,待登记传 0 * @param formEnum 物理位置 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param organizationId 入院科室ID * @param queryWrapper 查询条件 * @return 住院登记信息 */ IPage getInHospitalRegisterInfo(@Param("page") Page page, @Param("encounterClass") Integer encounterClass, @Param("encounterStatus") Integer encounterStatus, @Param("registeredFlag") String registeredFlag, @Param("formEnum") Integer formEnum, - @Param(Constants.WRAPPER) QueryWrapper queryWrapper, @Param("startTime") Date startTime, @Param("endTime") Date endTime, - @Param("organizationId") Long organizationId); + @Param("organizationId") Long organizationId, + @Param(Constants.WRAPPER) QueryWrapper queryWrapper); /** * 查询患者基本信息 diff --git a/healthlink-his-server/healthlink-his-application/src/main/resources/mapper/inhospitalnursestation/ATDManageAppMapper.xml b/healthlink-his-server/healthlink-his-application/src/main/resources/mapper/inhospitalnursestation/ATDManageAppMapper.xml index 72c8c9a7f..c1b7e729e 100755 --- a/healthlink-his-server/healthlink-his-application/src/main/resources/mapper/inhospitalnursestation/ATDManageAppMapper.xml +++ b/healthlink-his-server/healthlink-his-application/src/main/resources/mapper/inhospitalnursestation/ATDManageAppMapper.xml @@ -512,7 +512,7 @@ personal_account.balance_amount, personal_account.id AS account_id, T2.category_code, - ao.name AS org_name + COALESCE(ao.name, al1."name") AS org_name FROM med_medication_request AS T1 LEFT JOIN med_medication_definition AS T2 ON T2.id = T1.medication_id diff --git a/healthlink-his-ui/src/views/emergency/greentrack/index.vue b/healthlink-his-ui/src/views/emergency/greentrack/index.vue index 84e414c9c..2027c61ed 100644 --- a/healthlink-his-ui/src/views/emergency/greentrack/index.vue +++ b/healthlink-his-ui/src/views/emergency/greentrack/index.vue @@ -47,8 +47,8 @@ - - + + @@ -76,13 +76,15 @@ import {getPage,activate,complete,getStats,del} from './api' const tableData=ref([]);const total=ref(0);const stats=ref({}) const addVisible=ref(false);const completeVisible=ref(false) const addForm=ref({patientId:null,diseaseType:'',targetTime:90,doctor:''}) +const addFormRef=ref(null) +const addFormRules={patientId:[{required:true,message:'请选择患者',trigger:'blur'}]} const completeForm=ref({doorToTreatmentTime:60});let currentId=null const q=ref({pageNo:1,pageSize:20,diseaseType:'',isAchieved:null}) const loadData=async()=>{const r=await getPage(q.value);tableData.value=r.data?.records||[];total.value=r.data?.total||0} const refreshStats=async()=>{const r=await getStats({});stats.value=r.data||{}} const showAdd=()=>{addForm.value={patientId:null,diseaseType:'',targetTime:90,doctor:''};addVisible.value=true} const showComplete=(row)=>{currentId=row.id;completeForm.value={doorToTreatmentTime:60};completeVisible.value=true} -const submitAdd=async()=>{await activate(addForm.value);ElMessage.success('绿色通道已激活');addVisible.value=false;loadData();refreshStats()} +const submitAdd=async()=>{if(addFormRef.value){try{await addFormRef.value.validate()}catch{return}}await activate(addForm.value);ElMessage.success('绿色通道已激活');addVisible.value=false;loadData();refreshStats()} const doComplete=async()=>{await complete(currentId,completeForm.value);ElMessage.success('评估完成');completeVisible.value=false;loadData();refreshStats()} const delItem=async(id)=>{await del(id);ElMessage.success('已删除');loadData();refreshStats()} onMounted(()=>{loadData();refreshStats()}) diff --git a/healthlink-his-ui/src/views/inpatientNurse/inOut/components/transferOut.vue b/healthlink-his-ui/src/views/inpatientNurse/inOut/components/transferOut.vue index b8af720fd..7e0d527ba 100755 --- a/healthlink-his-ui/src/views/inpatientNurse/inOut/components/transferOut.vue +++ b/healthlink-his-ui/src/views/inpatientNurse/inOut/components/transferOut.vue @@ -1,4 +1,4 @@ -