From fce96ef468291fb9a0e6a4d34ab371315e9485d4 Mon Sep 17 00:00:00 2001
From: yxh <172933527@qq.com>
Date: 星期六, 03 十月 2026 00:59:44 +0800
Subject: [PATCH] ai优化

---
 jh-module-ecg/jh-module-ecg-biz/src/main/java/cn/lihu/jh/module/ecg/service/appointment/AppointmentServiceImpl.java | 1743 ++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 1,585 insertions(+), 158 deletions(-)

diff --git a/jh-module-ecg/jh-module-ecg-biz/src/main/java/cn/lihu/jh/module/ecg/service/appointment/AppointmentServiceImpl.java b/jh-module-ecg/jh-module-ecg-biz/src/main/java/cn/lihu/jh/module/ecg/service/appointment/AppointmentServiceImpl.java
index e126dce..fe54ef3 100644
--- a/jh-module-ecg/jh-module-ecg-biz/src/main/java/cn/lihu/jh/module/ecg/service/appointment/AppointmentServiceImpl.java
+++ b/jh-module-ecg/jh-module-ecg-biz/src/main/java/cn/lihu/jh/module/ecg/service/appointment/AppointmentServiceImpl.java
@@ -1,43 +1,47 @@
 package cn.lihu.jh.module.ecg.service.appointment;
 
+import cn.lihu.jh.framework.common.pojo.PageResult;
 import cn.lihu.jh.framework.common.util.date.DateUtils;
-import cn.lihu.jh.framework.common.util.date.LocalDateTimeUtils;
+import cn.lihu.jh.framework.common.util.object.BeanUtils;
+import cn.lihu.jh.module.ecg.controller.admin.appointment.vo.AppointmentConfirmReqVO;
+import cn.lihu.jh.module.ecg.controller.admin.appointment.vo.AppointmentPageReqVO;
+import cn.lihu.jh.module.ecg.controller.admin.appointment.vo.AppointmentSaveReqVO;
 import cn.lihu.jh.module.ecg.controller.admin.queue.vo.QueueSaveReqVO;
-import cn.lihu.jh.module.ecg.dal.dataobject.checktype.CheckTypeDO;
+import cn.lihu.jh.module.ecg.dal.dataobject.appointment.AppointmentDO;
 import cn.lihu.jh.module.ecg.dal.dataobject.patient.PatDetails;
+import cn.lihu.jh.module.ecg.dal.dataobject.queue.QueueDO;
+import cn.lihu.jh.module.ecg.dal.mysql.appointment.AppointmentMapper;
+import cn.lihu.jh.module.ecg.enums.AppointmentTypeEnum;
+import cn.lihu.jh.module.ecg.enums.HisCheckCodeEnum;
+import cn.lihu.jh.module.ecg.enums.HisCheckNameEnum;
+import cn.lihu.jh.module.ecg.enums.HisCheckStatusEnum;
 import cn.lihu.jh.module.ecg.feign.RemoteDataService;
-import cn.lihu.jh.module.ecg.feign.RestApiReqBodyVo;
-import cn.lihu.jh.module.ecg.feign.RestApiResult;
-import cn.lihu.jh.module.ecg.feign.dto.AppointmentExternal;
 import cn.lihu.jh.module.ecg.service.config.EcgConfigService;
+import cn.lihu.jh.module.ecg.service.devrent.ApplicationTemplate;
+import cn.lihu.jh.module.ecg.service.his.HisEcgFilter;
 import cn.lihu.jh.module.ecg.service.queue.QueueService;
-import cn.lihu.jh.module.ecg.service.queue.QueueServiceTxFunctions;
 import cn.lihu.jh.module.ecg.service.queuesequence.QueueSequenceService;
+import cn.lihu.jh.module.ecg.webservice.WebServiceClient;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.ibatis.annotations.Param;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.dao.DuplicateKeyException;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
 import org.springframework.validation.annotation.Validated;
 
-import cn.lihu.jh.module.ecg.controller.admin.appointment.vo.*;
-import cn.lihu.jh.module.ecg.dal.dataobject.appointment.AppointmentDO;
-import cn.lihu.jh.framework.common.pojo.PageResult;
-import cn.lihu.jh.framework.common.util.object.BeanUtils;
-
-import cn.lihu.jh.module.ecg.dal.mysql.appointment.AppointmentMapper;
-
 import javax.annotation.Resource;
-import javax.xml.crypto.Data;
-
+import java.text.SimpleDateFormat;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+import java.util.stream.Collectors;
 
 import static cn.lihu.jh.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.lihu.jh.framework.common.pojo.CommonResult.error;
 import static cn.lihu.jh.module.ecg.enums.ErrorCodeConstants.*;
 
 /**
@@ -49,6 +53,16 @@
 @Validated
 @Slf4j
 public class AppointmentServiceImpl implements AppointmentService {
+
+    /**
+     * 鎮h�呮�у埆銆屾湭鐭ャ�嶇殑鍏滃簳鍊笺��
+     * <p>
+     * {@code appointment.pat_gender} 鍦ㄦ暟鎹簱涓负 NOT NULL 涓旀棤榛樿鍊�
+     * 锛堝疄娴嬶細{@code Field 'pat_gender' doesn't have a default value}锛夈��
+     * 鑰� {@code S0405} 绛夋姤鏂囩殑鎬у埆瀛楁璺緞涓� {@code S0201ECG} 涓嶅悓銆佷笖鍙兘缂哄け锛�
+     * 瀹归敊鏂板鏃惰嫢鍙栦笉鍒帮紝鎸� 0锛堟湭鐭ワ級鍐欏叆锛岄伩鍏嶆彃鍏ュけ璐ャ��
+     */
+    private static final Byte UNKNOWN_GENDER = (byte) 0;
 
     @Resource
     private EcgConfigService ecgConfigService;
@@ -65,13 +79,22 @@
     @Resource
     private AppointmentMapper appointmentMapper;
 
+    /**
+     * 蹇冪數涓氬姟鍗曞垽瀹氬櫒銆�
+     * <p>
+     * 鐢ㄤ簬 {@code S0405}锛堝叏闄㈤�氱敤鐘舵�佹祦锛夌殑杩囨护锛氶潪蹇冪數鍗曞彧璁版棩蹇椼�佷笉钀藉簱锛�
+     * 閬垮厤鍚� appointment 琛ㄧ亴鍏ユ斁灏�/瓒呭0绛夊叾浠栫瀹ょ殑鏁版嵁銆�
+     */
+    @Resource
+    private HisEcgFilter hisEcgFilter;
+
+    @Autowired
+    private WebServiceClient webServiceClient;
+
     @Override
     public Long createAppointment(AppointmentSaveReqVO createReqVO) {
         // 鎻掑叆
         AppointmentDO appointment = BeanUtils.toBean(createReqVO, AppointmentDO.class);
-
-        appointment.setBookTime( LocalDateTime.now() );
-        appointment.setBookSrc( 1 );
 
         try {
             appointmentMapper.insert(appointment);
@@ -93,6 +116,42 @@
     }
 
     @Override
+    public void updateAppointmentStatus(AppointmentSaveReqVO updateReqVO) {
+        // 鏍¢獙瀛樺湪
+        validateAppointmentExists(updateReqVO.getId());
+
+        // 鑾峰彇瀹屾暣鐨勯绾︿俊鎭�
+        AppointmentDO appointment = appointmentMapper.selectById(updateReqVO.getId());
+        if (appointment == null) {
+            throw exception(APPOINTMENT_NOT_EXISTS);
+        }
+
+        // 鏇存柊鐘舵��
+        appointment.setStatus(updateReqVO.getStatus());
+
+        // 璋冪敤HIS绯荤粺鏇存柊鐘舵��
+        String action = "S0405";
+        ApplicationTemplate app = new ApplicationTemplate();
+        String statusName = AppointmentTypeEnum.getByType(appointment.getStatus());
+        String message = app.getXML(appointment.getApplyNo(), appointment.getStatus(), statusName, null, appointment.getPatDeptCode(), appointment.getPatDeptDesc(), appointment.getPatWardCode(), appointment.getPatWardDesc(), appointment.getPatBedNo(), appointment.getEpisodeId(), String.valueOf(appointment.getBookSrc()), appointment.getPatId(), appointment.getPatName());
+
+        try {
+            String response = webServiceClient.callJHFWTYRK(action, "", message);
+            if (response.contains("鏇存柊鎴愬姛")) {
+                // 鏇存柊鏈湴鏁版嵁搴�
+                appointmentMapper.updateById(appointment);
+
+
+            } else {
+                throw exception(APPOINTMENT_UPDATE_FAIL);
+            }
+        } catch (Exception e) {
+            log.error("[updateAppointmentStatus][鏇存柊棰勭害鐘舵�佸け璐� appointment({})]", appointment, e);
+            throw exception(APPOINTMENT_UPDATE_FAIL);
+        }
+    }
+
+    @Override
     public void deleteAppointment(Long id) {
         // 鏍¢獙瀛樺湪
         validateAppointmentExists(id);
@@ -111,11 +170,10 @@
         return appointmentMapper.selectById(id);
     }
 
-    // TODO 灏氫笉鏀寔, 涓�浣嶆偅鑰呭綋澶╀袱绉嶆鏌ラ」鐩殑鎯呭喌
     @Override
-    public AppointmentDO getCurAppointmentByPatId(String patId) {
-        List<AppointmentDO> appointmentDOList = appointmentMapper.getCurrentPatId( patId );
-        return 0 == appointmentDOList.size() ? null : appointmentDOList.get(0);
+    public AppointmentDO getCurAppointmentByPatIdAndCheckType(String patId, Integer checkType) {
+        AppointmentDO appointmentDO = appointmentMapper.getByPatAndCheckTypeAndBookDate(patId, checkType);
+        return appointmentDO;
     }
 
     /**
@@ -124,20 +182,34 @@
      */
     @Override
     public List<AppointmentDO> queryAndCreateAppointmentByPatId(String patCode) {
-        // 鍏堜粠鍖婚櫌骞冲彴鏌ヨ
-        List<AppointmentDO> appointmentExtermalList = getAppointmentExtermal( patCode );
+        // 浠庡尰闄㈠钩鍙版煡璇�
+        List<AppointmentDO> appointmentExtermalList = appointmentMapper.getCurrentCode(patCode);
 
-        for ( int appointmentIndex = 0; appointmentIndex < appointmentExtermalList.size(); appointmentIndex++ ) {
+        for (int appointmentIndex = 0; appointmentIndex < appointmentExtermalList.size(); appointmentIndex++) {
             AppointmentDO appointmentExtermal = appointmentExtermalList.get(appointmentIndex);
-            if (null == appointmentExtermal )
-                continue;
+            if (null == appointmentExtermal) continue;
 
-            // 浠嶥B鏌ュ埌棰勭害锛屾槸鍚﹀凡缁忓瓨鍦�
-            AppointmentDO appointmentDO = appointmentMapper.getByPatAndCheckTypeAndBookDate(appointmentExtermal.getPatId(), appointmentExtermal.getBookCheckType(), appointmentExtermal.getBookDate());
+            // 浠嶥B鏌ヨ棰勭害锛屾槸鍚﹀凡缁忓瓨鍦�
+            AppointmentDO appointmentDO = appointmentMapper.getByEpisodeIdAndApplyNo(appointmentExtermal.getEpisodeId(), appointmentExtermal.getApplyNo());
             if (null == appointmentDO) {
                 appointmentMapper.insert(appointmentExtermal);
             } else {
-                appointmentExtermal.setId( appointmentDO.getId() ); // 纭繚 杩斿洖鍊间繚鎶� appointment id
+                appointmentExtermal.setId(appointmentDO.getId()); // 纭繚 杩斿洖鍊煎寘鍚� appointment id
+
+                // 澶勭悊绗竴娆$‘璁ゆ椂杩樻病鏈夐绾︽椂闂达紝鍐嶆潵纭鏃跺凡鏈夐绾︽椂闂寸殑鎯呭喌
+                if (appointmentDO.getBookDate() != appointmentExtermal.getBookDate()) {
+                    appointmentMapper.updateById(appointmentExtermal);
+                }
+            }
+        }
+
+        // 鏌ヨ[queue琛╙琛ュ厖 book_seq_num 鍒拌繑鍥炲�间腑
+        if (!appointmentExtermalList.isEmpty()) {
+            List<Long> appointIdlist = appointmentExtermalList.stream().map(appointmentDO -> appointmentDO.getId()).toList();
+            List<QueueDO> simpleQueueDOList = queueService.selectBookSeqNumByAppointIdList(appointIdlist);
+            if (!simpleQueueDOList.isEmpty()) {
+                Map<Long, Integer> mapAppointIdVsBookSeqNo = simpleQueueDOList.stream().collect(Collectors.toMap(QueueDO::getAppointId, QueueDO::getBookSeqNum));
+                appointmentExtermalList.forEach(appointmentExterma -> appointmentExterma.setBookSeqNum(mapAppointIdVsBookSeqNo.get(appointmentExterma.getId())));
             }
         }
 
@@ -150,101 +222,146 @@
 
     @Override
     public PageResult<AppointmentDO> getAppointmentPage(AppointmentPageReqVO pageReqVO) {
+        //1. 鍏堟牴鎹畃atId鍘绘煡
+        PageResult<AppointmentDO> result = appointmentMapper.selectPage(pageReqVO);
+        log.info("--------------PATID    result鐨勫�间负锛歿}", result);
+        if (!result.getList().isEmpty()) {
+            return result;
+        }
+
+        if (StringUtils.isNotEmpty(pageReqVO.getPatId())) {
+            // 2. 鐢� applyNo 鏌ヨ
+            pageReqVO.setApplyNo(pageReqVO.getPatId());
+            pageReqVO.setPatId(null);
+            log.info("------------------ApplyNo    pageReqVO鐨勫�间负锛歿}", pageReqVO);
+            result = appointmentMapper.selectPage(pageReqVO);
+            if (!result.getList().isEmpty()) {
+                return result;
+            }
+
+            // 3. 鐢� episodeId 鏌ヨ
+            pageReqVO.setEpisodeId(pageReqVO.getApplyNo());
+            pageReqVO.setPatId(null);
+            pageReqVO.setApplyNo(null);
+            log.info("-----------EpisodeId    pageReqVO鐨勫�间负锛歿}", pageReqVO);
+        }
         return appointmentMapper.selectPage(pageReqVO);
     }
 
     @Override
     public List<AppointmentDO> getAppointmentExtermal(String patId) {
-        List<AppointmentDO> curAppointDOList = new ArrayList<AppointmentDO>();
-
-        RestApiReqBodyVo reqBodyVo = new RestApiReqBodyVo();
-        if (18 == patId.length()) {
-            reqBodyVo.setSfzh( patId );
-        } else {
-            reqBodyVo.setMzzyh(patId);
-        }
-
-        // QueryRisReportList   queryEcgRequest
-        RestApiResult<AppointmentExternal> result = remoteDataService.httpApi("queryEcgRequest", "ECG", "ECG", reqBodyVo);
-        if (0 == result.getRow().size()) {
-            return curAppointDOList;
-        }
-
-        for (int appointIndex=0; appointIndex < result.getRow().size(); appointIndex++ ) {
-            AppointmentExternal appointmentExternal = result.getRow().get( appointIndex );
-
-            AppointmentDO appointmentDO = BeanUtils.toBean(appointmentExternal, AppointmentDO.class);
-            appointmentDO.setApplyNo( appointmentExternal.getReqIdeApplyno() );
-            appointmentDO.setPatSrc( getCorrespondingPatientSource(appointmentExternal.getAdmTypeCode()) );
-            appointmentDO.setPatId(appointmentExternal.getPatientID()); // 鍐呭涓� 韬唤璇佸彿 鎴� 闂ㄨ瘖浣忛櫌鍙�
-            appointmentDO.setPatName(appointmentExternal.getPatName());
-            appointmentDO.setPatGender(Byte.valueOf(appointmentExternal.getPatgender()));
-            appointmentDO.setPatBirthday(DateUtils.ofUTC(appointmentExternal.getEncPatBirthDate()).toLocalDate());
-            appointmentDO.setPatIdentityId(appointmentExternal.getIdentityID());
-            appointmentDO.setPatAddr(appointmentExternal.getAddress());
-            appointmentDO.setPatDeptCode(appointmentExternal.getPatLocDeptCode());
-            appointmentDO.setPatDeptDesc(appointmentExternal.getPatLocDeptDesc());
-            appointmentDO.setPatWardCode(appointmentExternal.getPatLocWardCode());
-            appointmentDO.setPatWardDesc(appointmentExternal.getPatLocWardDesc());
-            appointmentDO.setPatMobile(appointmentExternal.getPhone());
-            appointmentDO.setPatBedNo(appointmentExternal.getEnBedno());
-            appointmentDO.setBookId(appointmentExternal.getReqIdeApplyno());
-            appointmentDO.setBookCheckType(getCorrespondingCheckType(appointmentExternal.getPlanDefItemList().getPlanDefItem().getPlanDefItemcode()));
-            appointmentDO.setBookTime(DateUtils.ofUTC(appointmentExternal.getReqAuthoredOn()));  // 寮�鍗曟椂闂�
-
-            // 鍏夊紑鍗曠殑鎯呭喌锛屾病鏈夐绾︽椂闂�
-            if (null != appointmentExternal.getReqExtBooktime() ) {
-                appointmentDO.setBookDate(DateUtils.ofUTC(appointmentExternal.getReqExtBooktime()).toLocalDate());
-                LocalDateTime bookStartTime = DateUtils.ofUTC(appointmentExternal.getReqExtBooktime());
-                LocalDateTime bookEndTime = bookStartTime.plusMinutes( ecgConfigService.getTimeslotLength());
-                appointmentDO.setBookTimeslot((bookStartTime.getHour() * 100 + bookStartTime.getMinute()) * 10000 + bookEndTime.getHour() * 100 + bookEndTime.getMinute());
-            }
-
-            appointmentDO.setBookSrc(0);
-            appointmentDO.setPaid(1);
-
-            curAppointDOList.add( appointmentDO );
-        }
-
-        return curAppointDOList;
+        return appointmentMapper.getCurrentPatId(patId);
     }
+
+//    @Override
+//    public List<AppointmentDO> getAppointmentExtermal(String patId) {
+//        List<AppointmentDO> curAppointDOList = new ArrayList<AppointmentDO>();
+//
+//        RestApiReqBodyVo reqBodyVo = new RestApiReqBodyVo();
+//        if (18 == patId.length()) {
+//            reqBodyVo.setSfzh( patId );
+//        } else {
+//            reqBodyVo.setMzzyh(patId);
+//        }
+//
+//        // QueryRisReportList   queryEcgRequest
+//        RestApiResult<AppointmentExternal> result = remoteDataService.httpApi("queryEcgRequest", "ECG", "ECG", reqBodyVo);
+//        if (0 == result.getRow().size()) {
+//            return curAppointDOList;
+//        }
+//
+//        for (int appointIndex=0; appointIndex < result.getRow().size(); appointIndex++ ) {
+//            AppointmentExternal appointmentExternal = result.getRow().get( appointIndex );
+//
+//            AppointmentDO appointmentDO = BeanUtils.toBean(appointmentExternal, AppointmentDO.class);
+//            appointmentDO.setApplyNo( appointmentExternal.getReqIdeApplyno() );
+//            appointmentDO.setEpisodeId( appointmentExternal.getEpisodeID() );
+//            appointmentDO.setPatSrc( getCorrespondingPatientSource(appointmentExternal.getAdmTypeCode()) );
+//            appointmentDO.setPatId(appointmentExternal.getPatientID()); // 鍐呭涓� 韬唤璇佸彿 鎴� 闂ㄨ瘖浣忛櫌鍙�
+//            appointmentDO.setPatName(appointmentExternal.getPatName());
+//            appointmentDO.setPatGender(Byte.valueOf(appointmentExternal.getPatgender()));
+//            appointmentDO.setPatBirthday(DateUtils.ofUTC(appointmentExternal.getEncPatBirthDate()).toLocalDate());
+//            appointmentDO.setPatIdentityId(appointmentExternal.getIdentityID());
+//            appointmentDO.setPatAddr(appointmentExternal.getAddress());
+//            appointmentDO.setPatDeptCode(appointmentExternal.getPatLocDeptCode());
+//            appointmentDO.setPatDeptDesc(appointmentExternal.getPatLocDeptDesc());
+//            appointmentDO.setPatWardCode(appointmentExternal.getPatLocWardCode());
+//            appointmentDO.setPatWardDesc(appointmentExternal.getPatLocWardDesc());
+//            appointmentDO.setPatMobile(appointmentExternal.getPhone());
+//            appointmentDO.setPatBedNo(appointmentExternal.getEnBedno());
+//            appointmentDO.setBookCheckType(getCorrespondingCheckType(appointmentExternal.getPlanDefItemList().getPlanDefItem().getPlanDefItemcode()));
+//            appointmentDO.setBookTime(DateUtils.ofUTC(appointmentExternal.getReqAuthoredOn()));  // 寮�鍗曟椂闂�
+//            appointmentDO.setBookSrc( 0 );
+//
+//            // 鍏夊紑鍗曠殑鎯呭喌锛屾病鏈夐绾︽椂闂�
+//            if (null != appointmentExternal.getReqExtBooktime() ) {
+//                appointmentDO.setBookDate(DateUtils.ofUTC(appointmentExternal.getReqExtBooktime()).toLocalDate());
+//                LocalDateTime bookStartTime = DateUtils.ofUTC(appointmentExternal.getReqExtBooktime());
+//                LocalDateTime bookEndTime = bookStartTime.plusMinutes( ecgConfigService.getTimeslotLength());
+//                appointmentDO.setBookTimeslot((bookStartTime.getHour() * 100 + bookStartTime.getMinute()) * 10000 + bookEndTime.getHour() * 100 + bookEndTime.getMinute());
+//            }
+//
+//            appointmentDO.setPaid( 0 );
+//
+//            curAppointDOList.add( appointmentDO );
+//        }
+//
+//        return curAppointDOList;
+//    }
 
     @Override
     @Transactional(rollbackFor = Exception.class)
     public Integer appoitmentConfirm(AppointmentConfirmReqVO confirmReqVO) {
         AppointmentDO appointment = getAppointment(confirmReqVO.getId());
-        if (null == appointment)
-            throw exception(PATIENT_NOT_EXISTS);
+        if (null == appointment) throw exception(PATIENT_NOT_EXISTS);
 
         // 鎵嬪姩棰勭害锛岀洿鎺ヨ缃绾︽棩鏈熶负褰撳ぉ
         if (confirmReqVO.getIsVip() == 1) {
-            appointment.setBookDate( LocalDate.now() );
+            appointment.setBookDate(LocalDate.now());
         }
 
-        if ( !DateUtils.isToday(appointment.getBookDate()) )
-            throw exception( APPOINTMENT_NOT_TODAY);
+        if (!DateUtils.isToday(appointment.getBookDate())) throw exception(APPOINTMENT_NOT_TODAY);
 
 
-        Integer newSeqNo = null;
-        if (confirmReqVO.getIsVip() == 0) {
-            newSeqNo = queueSequenceService.distributeSeqNo(confirmReqVO.getBookCheckType(), confirmReqVO.getBookTimeslot());
-        } else {
-            newSeqNo = queueSequenceService.distributeVipSeqNo(confirmReqVO.getBookCheckType(), confirmReqVO.getBookTimeslot());
-        }
+        // 銆愰渶姹傦細鎺掗槦搴忓彿瑙勫垯銆戞寜 check_type.seq_rule 鍐冲畾鍙栧彿鏂瑰紡锛�
+        //   0=棰勭害鍙凤紙鐢ㄩ绾︾郴缁熷凡涓嬪彂鐨勫彿锛涘綋鍓� HIS 鏈笅鍙戯紝浼氳嚜鍔ㄥ洖閫�鍒扮鍒板簭鍙凤級
+        //   1=绛惧埌搴忓彿锛堝綋澶╁悓妫�鏌ョ被鍨嬭繛缁�掑锛屾瘡 9 涓鐣� 1 涓粰鍔犳�ワ級
+        //   2=鏃舵鍙凤紙鎸� bookTimeslot 浠庡彿姹犲彇鍙� 鈥斺�� 鏀归�犲墠鐨勫師鏈夎涓猴紝涔熸槸鏈厤缃椂鐨勯粯璁わ級
+        boolean urgent = isHisUrgent(appointment);
+        Integer newSeqNo = queueSequenceService.distributeSeqNoByRule(
+                confirmReqVO.getBookCheckType(),
+                confirmReqVO.getBookTimeslot(),
+                appointment.getBookSeqNum(),
+                urgent);
+        log.info("[appoitmentConfirm][鍒嗛厤鎺掗槦搴忓彿] appointmentId={} checkType={} timeslot={} 瑙勫垯鍙栧彿={} 鍔犳��={}",
+                appointment.getId(), confirmReqVO.getBookCheckType(), confirmReqVO.getBookTimeslot(),
+                newSeqNo, urgent);
 
         try {
             QueueSaveReqVO queueSaveReqVO = new QueueSaveReqVO();
+            queueSaveReqVO.setApplyNo(appointment.getApplyNo());
+            queueSaveReqVO.setEpisodeId(appointment.getEpisodeId());
+            queueSaveReqVO.setAppointId(appointment.getId());
             queueSaveReqVO.setPatId(appointment.getPatId());
             queueSaveReqVO.setPatName(appointment.getPatName());
             queueSaveReqVO.setPatGender(appointment.getPatGender());
             queueSaveReqVO.setBookDate(appointment.getBookDate());
-            queueSaveReqVO.setBookTimeslot(appointment.getBookTimeslot());
+            queueSaveReqVO.setBookTimeslot(confirmReqVO.getBookTimeslot()); /*appointment.getBookTimeslot()*/
             queueSaveReqVO.setBookCheckType(appointment.getBookCheckType());
-            queueSaveReqVO.setBookSeqNum( newSeqNo );
-            queueSaveReqVO.setIsVip( confirmReqVO.getIsVip() );
+            queueSaveReqVO.setBookSeqNum(newSeqNo);
+            queueSaveReqVO.setIsVip(confirmReqVO.getIsVip());
             queueSaveReqVO.setPassed((byte) 0);
             queueSaveReqVO.setExpired((byte) 0);
-            queueSaveReqVO.setPatDetails( getPatDetails(appointment) );
+            // 闇�姹�2锛欼SCB銆屾槸鍚﹀簥杈广�嶉殢闃熷垪涓�璧疯惤搴擄紝渚涘睆骞�/椤甸潰灞曠ず
+            queueSaveReqVO.setIsBedside(appointment.getIsBedside());
+            // HIS 鍔犳�ユ爣璁帮紙jjbz=J锛夆啋 鎺掗槦鍔犳�ワ紝涓庝汉宸ユ彃闃熷叡鐢� jump_flag銆�
+            // queue 琛ㄦ寜 "order by jump_flag desc, book_timeslot, create_time" 鍙栧彿锛岀疆 1 鍗充紭鍏堛��
+            if (urgent) {
+                queueSaveReqVO.setJumpFlag((byte) 1);
+                log.info("[appoitmentConfirm][HIS 鍔犳�ユ偅鑰咃紝鎺掗槦鏍囪涓哄姞鎬 applyNo={} patId={}",
+                        appointment.getApplyNo(), appointment.getPatId());
+            }
+            queueSaveReqVO.setPatDetails(getPatDetails(appointment));
             queueService.queue(queueSaveReqVO);
         } catch (DuplicateKeyException duplicateKeyException) {
             throw exception(APPOINTMENT_HAVE_QUEUED);
@@ -253,65 +370,1375 @@
         return newSeqNo;
     }
 
-    private Integer getCorrespondingCheckType(String strPlanDefItemcode) {
-        if (strPlanDefItemcode.equals("691133607"))
-            return 100;
-        else if (strPlanDefItemcode.equals("201605"))
-            return 200;
-        else if (strPlanDefItemcode.equals("200327"))
-            return 300;
-        else if (strPlanDefItemcode.equals("201652"))
-            return 400;
-        else if (strPlanDefItemcode.equals("502490914"))
-            return 500;
-        else if (strPlanDefItemcode.equals("419562119"))
-            return 600;
-        else if (strPlanDefItemcode.equals("201604"))
-            return 700;
-        else if (strPlanDefItemcode.equals("1202042"))
-            return 800;
-        else if (strPlanDefItemcode.equals("1202058"))
-            return 900;
-        else if (strPlanDefItemcode.equals("1202065"))
-            return 1000;
-        else if (strPlanDefItemcode.equals("559542128"))
-            return 1100;
-        else if (strPlanDefItemcode.equals("590244511"))
-            return 1200;
-        else if (strPlanDefItemcode.equals("666454217"))
-            return 1300;
-        else if (strPlanDefItemcode.equals("720791490"))
-            return 1400;
-        else if (strPlanDefItemcode.equals("720792077"))
-            return 1500;
-
-        return 100;
+    /**
+     * 鍒ゆ柇璇ラ绾︽槸鍚︿负 HIS 鏍囪鐨勫姞鎬ュ崟銆�
+     * <p>
+     * 渚濇嵁 {@code appointment.jjbz}锛堟潵婧� HIS 鎶ユ枃 {@code zdy.jjbz}锛夛細
+     * {@code J} / {@code 1} / {@code Y} 瑙嗕负鍔犳�ワ紝鍏朵綑锛堝 {@code N}锛変负甯歌銆�
+     * <p>
+     * <b>娉ㄦ剰</b>锛氬疄娴� 2026-09-17 鏃ュ織涓� 60 鏉� S0201ECG 鐨� {@code jjbz} **鍏ㄤ负 N**锛�
+     * 鏈彇寰� {@code J} 鐨勭湡瀹炴牱渚嬶紝鍥犳璇ュ垎鏀皻鏈粡杩囩湡瀹炴姤鏂囬獙璇併��
+     *
+     * @param appointment 棰勭害璁板綍锛屽彲涓� null
+     * @return true=鍔犳��
+     */
+    private boolean isHisUrgent(AppointmentDO appointment) {
+        if (appointment == null || StringUtils.isEmpty(appointment.getJjbz())) {
+            return false;
+        }
+        String flag = appointment.getJjbz().trim();
+        return "J".equalsIgnoreCase(flag) || "1".equals(flag) || "Y".equalsIgnoreCase(flag);
     }
 
-    private Integer getCorrespondingPatientSource(String admTypeCode) {
-        if (admTypeCode.equals("AMB"))
-            return 1;
-        else if (admTypeCode.equals("EMER"))
-            return 2;
-        else if (admTypeCode.equals("IMP"))
-            return 3;
-        else if (admTypeCode.equals("PHY"))
-            return 4;
-
-        return 0;
-    }
-
-    private PatDetails getPatDetails(AppointmentDO appointment) {
-        PatDetails patDetails = new PatDetails();
-        patDetails.setId( appointment.getPatId() );
-        patDetails.setName( appointment.getPatName() );
-        patDetails.setMobile( appointment.getPatMobile() );
-        patDetails.setDeptCode( appointment.getPatDeptCode() );
-        patDetails.setDeptDesc( appointment.getPatDeptDesc() );
-        patDetails.setWardCode( appointment.getPatWardCode() );
-        patDetails.setWardDesc( appointment.getPatWardDesc() );
-        patDetails.setBedNo( appointment.getPatBedNo() );
+    private PatDetails getPatDetails(AppointmentDO appointment) {        PatDetails patDetails = new PatDetails();
+        patDetails.setAppointmentId(appointment.getId());
+        patDetails.setId(appointment.getPatId());
+        patDetails.setName(appointment.getPatName());
+        patDetails.setMobile(appointment.getPatMobile());
+        patDetails.setDeptCode(appointment.getPatDeptCode());
+        patDetails.setDeptDesc(appointment.getPatDeptDesc());
+        patDetails.setWardCode(appointment.getPatWardCode());
+        patDetails.setWardDesc(appointment.getPatWardDesc());
+        patDetails.setBedNo(appointment.getPatBedNo());
+        patDetails.setSource(appointment.getPatSrc());
         return patDetails;
     }
+
+    @Override
+    public Long handleAppointmentCreate(Map<String, Object> dataMap) {
+        if (dataMap == null) {
+            throw exception(APPOINTMENT_CREATE_FAIL);
+        }
+
+        try {
+            // 鍒涘缓AppointmentDO瀵硅薄
+            AppointmentDO appointment = new AppointmentDO();
+
+            // 浠巇ataMap涓彁鍙栨暟鎹�
+            Map<String, Object> encounter = extractEncounter(dataMap);
+            if (encounter == null) {
+                throw exception(APPOINTMENT_CREATE_FAIL);
+            }
+
+            // 璁剧疆灏辫瘖娴佹按鍙�
+            Optional.ofNullable(encounter.get("id")).map(id -> (Map<String, Object>) id).map(id -> (List<?>) id.get("item")).filter(items -> items.size() > 1).map(items -> (Map<String, Object>) items.get(1)).map(item -> (String) item.get("extension")).ifPresent(appointment::setEpisodeId);
+
+            Optional.ofNullable(encounter.get("location")).map(location -> (Map<String, Object>) location).map(location -> (Map<String, Object>) location.get("serviceDeliveryLocation")).map(serviceDeliveryLocation -> (Map<String, Object>) serviceDeliveryLocation.get("location")).map(location -> (Map<String, Object>) location.get("id")).map(id -> (Map<String, Object>) id.get("item")).map(item -> (String) item.get("extension")).ifPresent(appointment::setPatBedNo);
+
+            Optional.ofNullable(dataMap.get("controlActProcess")).map(controlActProcess -> (Map<String, Object>) controlActProcess).map(controlActProcess -> (Map<String, Object>) controlActProcess.get("subject")).map(subject -> (Map<String, Object>) subject.get("observationRequest")).map(observationRequest -> (Map<String, Object>) observationRequest.get("zdy")).map(zdy -> {
+                Object ISDN = zdy.get("ISDN");
+                if (ISDN != null) {
+                    return ISDN.toString();
+                }
+                return null;
+            }).ifPresent(appointment::setTolerance);
+
+            // 鏄惁搴婅竟锛堥渶姹�2锛夛細zdy.ISCB銆�0=鍚︼紝1=鏄��
+            //
+            // 涓� ISDN 鍚屽湪 zdy 鑷畾涔夊瓧娈靛尯锛屼笖**鍚屽睘瀹樻柟鏂囨。鏈畾涔夌殑 7 涓墿灞曞瓧娈�**
+            // 锛堝疄娴嬬湡瀹炴姤鏂� 26 涓瓧娈碉紝鏂囨。鍙畾涔変簡 19 涓級銆�
+            // 瑙f瀽閫昏緫鎶藉埌 findBedsideFlag锛屼究浜庡崟鍏冩祴璇曘��
+            String bedsideFlag = findBedsideFlag(dataMap);
+            if (bedsideFlag != null) {
+                appointment.setIsBedside(bedsideFlag);
+            }
+
+            // 浼樺厛绾�/鍔犳�ユ爣璁帮紙zdy.jjbz锛歂=甯歌锛孞=鍔犳�ワ級
+            Map<String, Object> zdyNode = dig(dataMap, "controlActProcess", "subject",
+                    "observationRequest", "zdy");
+            if (zdyNode != null) {
+                Object jjbz = zdyNode.get("jjbz");
+                if (jjbz != null && StringUtils.isNotEmpty(jjbz.toString().trim())) {
+                    appointment.setJjbz(jjbz.toString().trim());
+                }
+            }
+
+            // 妫�鏌ユ柟寮忕紪鐮侊細methodCode.item[0].code
+            // 绫诲瀷瀹夊叏锛歮ethodCode 鐨� item 鍙兘涓嶅瓨鍦紝鍙兘鏄崟涓� Map锛屼篃鍙兘鏄� List
+            Map<String, Object> c2 = dig(dataMap, "controlActProcess", "subject",
+                    "observationRequest", "component2", "observationRequest");
+            if (c2 != null) {
+                String methodCode = digText(c2, "methodCode", "item", "code");
+                if (StringUtils.isEmpty(methodCode)) {
+                    // 浼� List 鏃� digText 浼氳嚜鍔ㄥ彇棣栧厓绱犲啀涓嬮捇锛岃繖閲屽啀鍏滀竴娆� code
+                    methodCode = digText(c2, "methodCode", "item", "part", "value");
+                }
+                if (StringUtils.isNotEmpty(methodCode)) {
+                    appointment.setMethodCode(methodCode);
+                }
+                String targetSite = digText(c2, "targetSiteCode", "item", "code");
+                if (StringUtils.isNotEmpty(targetSite)) {
+                    appointment.setTargetSiteCode(targetSite);
+                }
+
+                // 鍖诲槺鍙凤細component2.observationRequest.id.item[root=2.16.156.10011.1.28].extension
+                //
+                // 渚濇嵁锛圚IS銆婁氦浜掓湇鍔¢�氱敤鎺ュ彛鏂囨。銆嬶級锛�
+                //   路 6.1 鐢宠鍗曟柊澧炴湇鍔★紙鏈」鐩墍鐢� S0201ECG锛夌殑绀轰緥涓鑺傜偣娉ㄩ噴鍗充负銆屽尰鍢盜D銆嶏細
+                //       <id><!--鍖诲槺ID--><item root="2.16.156.10011.1.28" extension=""/></id>
+                //   路 6.4/6.5 妫�鏌ラ绾︾姸鎬佹湇鍔$殑绀轰緥涓悓鏍锋敞閲婁负銆屽尰鍢卞彿銆嶏細
+                //       <!--  鍖诲槺鍙�  --> ... <id root="2.16.156.10011.1.28" extension="11"/>
+                //   瀹炴祴 S0201ECG 220 鏉°�丼0202ECG 29 鏉�**鍧囨惡甯﹁鍊间笖鏃犵┖鍊�**
+                //   锛堝舰濡� 112663505 / 112663700锛屼笖鍚屼竴鍖诲槺鍦ㄦ柊澧炰笌鏇存柊鎶ユ枃涓彇鍊间竴鑷达級銆�
+                //
+                // 銆愪负浠�涔堜笉鐢� digText 鐩存帴鍙� item.extension銆�
+                //   digText 閬囧埌 List **鍙彇棣栧厓绱�**锛涜�� id.item 鍦ㄩ儴鍒嗘湇鍔′腑鏄暟缁�
+                //   锛堝 6.4 鍚屾椂鍚� 1.24 鐢宠鍗曞彿涓� 1.28 鍖诲槺鍙凤級銆傛晠蹇呴』**鎸� root 鍖归厤**锛�
+                //   鍚﹀垯鍙兘鎶婄敵璇峰崟鍙峰綋鎴愬尰鍢卞彿銆�
+                String doctorAdviceNo = findExtensionByRoot(c2.get("id"), ORDER_NO_ROOT);
+                if (StringUtils.isNotEmpty(doctorAdviceNo)) {
+                    appointment.setDoctorAdviceNo(doctorAdviceNo);
+                }
+            }
+
+            // 鍖荤敓寮�鍗曟椂闂�
+            Optional.ofNullable(dataMap.get("controlActProcess")).map(controlActProcess -> (Map<String, Object>) controlActProcess).map(controlActProcess -> (Map<String, Object>) controlActProcess.get("subject")).map(subject -> (Map<String, Object>) subject.get("observationRequest")).map(observationRequest -> (Map<String, Object>) observationRequest.get("author")).map(author -> (Map<String, Object>) author.get("time")).map(time -> (String) time.get("value")).map(str -> {
+                try {
+                    return new SimpleDateFormat("yyyyMMddHHmmss").parse(str);
+                } catch (Exception e) {
+                    e.printStackTrace();
+                    return null;
+                }
+            }).filter(Objects::nonNull).ifPresent(appointment::setDoctorTime);
+
+            //璁剧疆寮�鍗曞尰鐢熷鍚�
+            Optional.ofNullable(dataMap.get("controlActProcess")).map(controlActProcess -> (Map<String, Object>) controlActProcess).map(controlActProcess -> (Map<String, Object>) controlActProcess.get("subject")).map(subject -> (Map<String, Object>) subject.get("observationRequest")).map(observationRequest -> (Map<String, Object>) observationRequest.get("author")).map(author -> (Map<String, Object>) author.get("assignedEntity")).map(assignedEntity -> (Map<String, Object>) assignedEntity.get("assignedPerson")).map(assignedPerson -> (Map<String, Object>) assignedPerson.get("name")).map(name -> (Map<String, Object>) name.get("item")).map(item -> (Map<String, Object>) item.get("part")).map(part -> (String) part.get("value")).ifPresent(appointment::setDoctor);
+
+            //璁剧疆寮�鍗曞尰鐢熺紪鐮�
+            Optional.ofNullable(dataMap.get("controlActProcess")).map(controlActProcess -> (Map<String, Object>) controlActProcess).map(controlActProcess -> (Map<String, Object>) controlActProcess.get("subject")).map(subject -> (Map<String, Object>) subject.get("observationRequest")).map(observationRequest -> (Map<String, Object>) observationRequest.get("author")).map(author -> (Map<String, Object>) author.get("assignedEntity")).map(assignedEntity -> (Map<String, Object>) assignedEntity.get("id")).map(id -> (Map<String, Object>) id.get("item")).map(item -> (String) item.get("extension")).ifPresent(appointment::setDoctorCode);
+
+
+            // 璁剧疆鎮h�呮潵婧愮被鍨�
+            Optional.ofNullable(encounter.get("code")).map(code -> (Map<String, Object>) code).map(code -> (String) code.get("code")).map(Integer::parseInt).ifPresent(appointment::setPatSrc);
+
+            // 璁剧疆鎮h�呬俊鎭�
+            Optional.ofNullable(encounter.get("subject")).map(subject -> (Map<String, Object>) subject).map(subject -> (Map<String, Object>) subject.get("patient")).map(patient -> (Map<String, Object>) patient.get("patientPerson")).ifPresent(patientPerson -> {
+                // 璁剧疆鎮h�匢D
+                Optional.ofNullable(encounter.get("subject")).map(subject -> (Map<String, Object>) subject).map(subject -> (Map<String, Object>) subject.get("patient")).map(patient -> (Map<String, Object>) patient.get("id")).map(id -> (Map<String, Object>) id).map(id -> (List<?>) id.get("item")).filter(items -> items.size() > 1).map(items -> (Map<String, Object>) items.get(1)).map(item -> (String) item.get("extension")).ifPresent(appointment::setPatId);
+
+                // 璁剧疆鎮h�呴棬璇婂彿
+                Optional.ofNullable(encounter.get("subject")).map(subject -> (Map<String, Object>) subject).map(subject -> (Map<String, Object>) subject.get("patient")).map(patient -> (Map<String, Object>) patient.get("id")).map(id -> (Map<String, Object>) id).map(id -> (List<?>) id.get("item")).filter(items -> items.size() > 1).map(items -> (Map<String, Object>) items.get(2)).map(item -> (String) item.get("extension")).ifPresent(appointment::setOutpatientNo);
+
+                // 璁剧疆鎮h�呬綇闄㈠彿
+                Optional.ofNullable(encounter.get("subject")).map(subject -> (Map<String, Object>) subject).map(subject -> (Map<String, Object>) subject.get("patient")).map(patient -> (Map<String, Object>) patient.get("id")).map(id -> (Map<String, Object>) id).map(id -> (List<?>) id.get("item")).filter(items -> items.size() > 1).map(items -> (Map<String, Object>) items.get(3)).map(item -> (String) item.get("extension")).ifPresent(appointment::setHospitalNo);
+
+                // 璁剧疆鎮h�呭鍚�
+                Optional.ofNullable(patientPerson.get("name")).map(name -> (Map<String, Object>) name).map(name -> (Map<String, Object>) name.get("item")).map(item -> (Map<String, Object>) item.get("part")).map(part -> (String) part.get("value")).ifPresent(appointment::setPatName);
+
+                // 璁剧疆鎮h�呮墜鏈哄拰鐢佃瘽
+                Optional.ofNullable(patientPerson.get("telecom")).map(telecom -> (Map<String, Object>) telecom).map(telecom -> (Map<String, Object>) telecom.get("item")).map(item -> (String) item.get("value")).ifPresent(phone -> {
+                    appointment.setPatMobile(phone);
+                    appointment.setPatPhone(phone);
+                });
+
+                // 璁剧疆鎮h�呮�у埆
+                Optional.ofNullable(patientPerson.get("administrativeGenderCode")).map(genderCode -> (Map<String, Object>) genderCode).map(genderCode -> (String) genderCode.get("code")).map(Byte::parseByte).ifPresent(appointment::setPatGender);
+
+                // 璁剧疆鎮h�呯敓鏃�
+                Optional.ofNullable(patientPerson.get("birthTime")).map(birthTime -> (Map<String, Object>) birthTime).map(birthTime -> (String) birthTime.get("value")).map(birthdayStr -> LocalDate.parse(birthdayStr, DateTimeFormatter.ofPattern("yyyyMMdd"))).ifPresent(appointment::setPatBirthday);
+
+                // 璁剧疆鎮h�呭湴鍧�
+                Optional.ofNullable(patientPerson.get("addr")).map(addr -> (Map<String, Object>) addr).map(addr -> (Map<String, Object>) addr.get("item")).map(item -> (Map<String, Object>) item.get("part")).map(part -> (String) part.get("value")).ifPresent(appointment::setPatAddr);
+
+                // 璁剧疆鎮h�呰韩浠借瘉鍙�
+                Optional.ofNullable(patientPerson.get("id")).map(id -> (Map<String, Object>) id).map(id -> (List<?>) id.get("item")).filter(items -> !items.isEmpty()).map(items -> (Map<String, Object>) items.get(0)).map(item -> (String) item.get("extension")).ifPresent(appointment::setPatIdentityId);
+
+                //鎮h�呭尰淇濆崱
+                Optional.ofNullable(patientPerson.get("id")).map(id -> (Map<String, Object>) id).map(id -> (List<?>) id.get("item")).filter(items -> !items.isEmpty()).map(items -> (Map<String, Object>) items.get(1)).map(item -> (String) item.get("extension")).ifPresent(appointment::setMedicalCard);
+            });
+
+            // 璁剧疆鏀粯鐘舵��
+            Optional.ofNullable(dataMap.get("controlActProcess")).map(controlActProcess -> (Map<String, Object>) controlActProcess).map(controlActProcess -> (Map<String, Object>) controlActProcess.get("subject")).map(subject -> (Map<String, Object>) subject.get("observationRequest")).map(observationRequest -> (Map<String, Object>) observationRequest.get("zdy")).map(zdy -> {
+                Object payStatus = zdy.get("PayStatus");
+                if (payStatus != null) {
+                    try {
+                        return Integer.parseInt(payStatus.toString());
+                    } catch (NumberFormatException e) {
+                        log.warn("PayStatus杞崲澶辫触: {}", payStatus);
+                        return null;
+                    }
+                }
+                return null;
+            }).ifPresent(appointment::setPaid);
+            // 璁剧疆鐢宠鍗曞彿
+            Optional.ofNullable(dataMap.get("controlActProcess")).map(controlActProcess -> (Map<String, Object>) controlActProcess).map(controlActProcess -> (Map<String, Object>) controlActProcess.get("subject")).map(subject -> (Map<String, Object>) subject.get("observationRequest")).map(observationRequest -> (Map<String, Object>) observationRequest.get("id")).map(id -> (Map<String, Object>) id.get("item")).map(item -> (String) item.get("extension")).ifPresent(appointment::setApplyNo);
+
+            //璁剧疆妫�鏌ョ被鍨�
+//            String checkType = Optional.ofNullable(dataMap.get("controlActProcess")).map(controlActProcess -> (Map<String, Object>) controlActProcess).map(controlActProcess -> (Map<String, Object>) controlActProcess.get("component2")).map(component2 -> (Map<String, Object>) component2.get("observationRequest")).map(observationRequest -> (Map<String, Object>) observationRequest.get("code")).map(code -> (String) code.get("code")).orElseThrow(() -> exception(APPOINTMENT_CREATE_FAIL));
+            String checkType = Optional.ofNullable(dataMap.get("controlActProcess")).map(controlActProcess -> (Map<String, Object>) controlActProcess).map(controlActProcess -> (Map<String, Object>) controlActProcess.get("subject")).map(subject -> (Map<String, Object>) subject.get("observationRequest")).map(observationRequest -> (Map<String, Object>) observationRequest.get("component2")).map(component2 -> (Map<String, Object>) component2.get("observationRequest")).map(observationRequest -> (Map<String, Object>) observationRequest.get("code")).map(code -> (String) code.get("code")).orElseThrow(() -> exception(APPOINTMENT_CREATE_FAIL));
+            String checkName = Optional.ofNullable(dataMap.get("controlActProcess")).map(controlActProcess -> (Map<String, Object>) controlActProcess).map(controlActProcess -> (Map<String, Object>) controlActProcess.get("subject")).map(subject -> (Map<String, Object>) subject.get("observationRequest")).map(observationRequest -> (Map<String, Object>) observationRequest.get("component2")).map(component2 -> (Map<String, Object>) component2.get("observationRequest")).map(observationRequest -> (Map<String, Object>) observationRequest.get("code")).map(code -> (Map<String, Object>) code.get("displayName")).map(displayName -> (String) displayName.get("value")).orElseThrow(() -> exception(APPOINTMENT_CREATE_FAIL));
+            // 渚濇嵁妫�鏌ョ被鍨嬭В鏋愭湰鍦� book_check_type銆�
+            // 淇鐐癸紙鍘熷疄鐜版湁缂洪櫡锛夛細
+            //   1. 鍒ゆ柇鍐欐垚 `checkType != null || checkType != ""`锛屾亽涓� true锛�
+            //   2. patSrc 涓� Integer锛宍!= 4` 鍦ㄨ秴鍑� Integer 缂撳瓨鑼冨洿鏃朵細澶辨晥锛屾敼鐢� Objects.equals锛�
+            //   3. HIS 浠g爜鍙兘鏄犲皠涓嶅埌锛岀洿鎺� Integer.valueOf(null) 浼氭姏
+            //      NumberFormatException锛堝疄娴嬫棩蹇椾腑鏈� "Cannot parse null string"锛夈��
+            if (Objects.equals(appointment.getPatSrc(), 4)) {
+                String normalizedName = StringUtils.isNotEmpty(checkName)
+                        ? checkName.replaceAll("\\s+", "") : null;
+                String mapped = StringUtils.isNotEmpty(normalizedName)
+                        ? HisCheckNameEnum.getByName(normalizedName) : null;
+                Integer value = parseIntegerQuietly(mapped);
+                if (value != null) {
+                    appointment.setBookCheckType(value);
+                } else {
+                    log.warn("[handleAppointmentCreate][浣撴鎮h�呮鏌ラ」鐩悕绉版棤娉曟槧灏刔 checkName={}", checkName);
+                }
+            } else {
+                Integer value = resolveBookCheckTypeFromComponent2(dataMap);
+                if (value != null) {
+                    appointment.setBookCheckType(value);
+                } else {
+                    log.warn("[handleAppointmentCreate][HIS 妫�鏌ヤ唬鐮佹棤娉曟槧灏勫埌鏈湴妫�鏌ョ被鍨媇 checkType={}", checkType);
+                }
+            }
+
+
+            // 璁剧疆绉戝鍜岀梾鍖轰俊鎭�
+            Optional.ofNullable(encounter.get("location")).map(location -> (Map<String, Object>) location).map(location -> (Map<String, Object>) location.get("serviceDeliveryLocation")).map(location -> (Map<String, Object>) location.get("serviceProviderOrganization")).ifPresent(org -> {
+                // 璁剧疆绉戝浠g爜鍜屽悕绉�
+                Optional.ofNullable(org.get("id")).map(id -> (Map<String, Object>) id).map(id -> (Map<String, Object>) id.get("item")).map(item -> (String) item.get("extension")).ifPresent(appointment::setPatDeptCode);
+
+                Optional.ofNullable(org.get("name")).map(name -> (Map<String, Object>) name).map(name -> (Map<String, Object>) name.get("item")).map(item -> (Map<String, Object>) item.get("part")).map(part -> (String) part.get("value")).ifPresent(appointment::setPatDeptDesc);
+
+                // 璁剧疆鐥呭尯浠g爜鍜屽悕绉�
+                Optional.ofNullable(org.get("asOrganizationPartOf")).map(wardOrg -> (Map<String, Object>) wardOrg).map(wardOrg -> (Map<String, Object>) wardOrg.get("wholeOrganization")).ifPresent(wardOrg -> {
+                    Optional.ofNullable(wardOrg.get("id")).map(id -> (Map<String, Object>) id).map(id -> (Map<String, Object>) id.get("item")).map(item -> (String) item.get("extension")).ifPresent(appointment::setPatWardCode);
+                    if (appointment.getPatSrc() != null && appointment.getPatSrc() == 4) {
+                        //浣撴灏辫繖涓�涓瀹�
+                        appointment.setPatDeptDesc("鍋ュ悍绠$悊涓績");
+                    } else {
+                        Optional.ofNullable(wardOrg.get("name")).map(name -> (Map<String, Object>) name).map(name -> (Map<String, Object>) name.get("item")).map(item -> (Map<String, Object>) item.get("part")).map(part -> (String) part.get("value")).ifPresent(appointment::setPatWardDesc);
+                    }
+                });
+            });
+
+
+            // 灏咥ppointmentDO杞崲涓篈ppointmentSaveReqVO骞朵繚瀛�
+            AppointmentSaveReqVO saveReqVO = BeanUtils.toBean(appointment, AppointmentSaveReqVO.class);
+
+            // 鎸夌敵璇峰崟鍙锋鏌ユ槸鍚﹀凡瀛樺湪銆�
+            // 銆愭湰娆℃敼鍔� Q5銆戝師瀹炵幇鍛戒腑宸插瓨鍦ㄦ椂鐩存帴 return锛堜笉鏇存柊锛夛紝
+            // 涓� S0202ECG / S0405 涓ゆ潯鏇存柊娴佺殑"鎵句笉鍒板垯鍏滃簳鏂板"鏂瑰悜鐩稿弽锛岃涔変笉缁熶竴銆�
+            // 鐜版敼涓猴細鍛戒腑宸插瓨鍦ㄥ垯浠ユ湰娆℃姤鏂囧唴瀹规墽琛屾洿鏂帮紝閬垮厤 HIS 閲嶅鎺ㄩ�佹椂鏃ф暟鎹笉琚慨姝c��
+            AppointmentDO appointmentDO = appointmentMapper.getByApplyNo(saveReqVO.getApplyNo());
+            log.info("[handleAppointmentCreate][鏄惁宸插瓨鍦╙ applyNo={} existingId={}",
+                    saveReqVO.getApplyNo(), appointmentDO == null ? null : appointmentDO.getId());
+            if (appointmentDO != null) {
+                // 淇濈暀鍘熻褰曚腑鏈鎶ユ枃鏈彁渚涚殑瀛楁锛岄伩鍏嶈绌哄�艰鐩�
+                mergeUnsetFields(appointment, appointmentDO);
+                saveReqVO = BeanUtils.toBean(appointment, AppointmentSaveReqVO.class);
+                saveReqVO.setId(appointmentDO.getId());
+                updateAppointment(saveReqVO);
+                return appointmentDO.getId();
+            }
+
+            return createAppointment(saveReqVO);
+        } catch (Exception e) {
+            log.error("[handleAppointmentCreate][澶勭悊棰勭害鍒涘缓澶辫触 dataMap({})]", dataMap, e);
+            throw exception(APPOINTMENT_CREATE_FAIL);
+        }
+    }
+
+    /**
+     * 鏇存柊鍓嶅悎骞讹細鏈鎶ユ枃鏈彁渚涳紙涓� null锛夌殑瀛楁锛屾部鐢ㄦ暟鎹簱涓殑鍘熷�笺��
+     * <p>
+     * 鐩殑锛歿@code S0201ECG} 鐨勬煇浜涘瓧娈靛湪閮ㄥ垎鍦烘櫙涓嬩細缂哄け锛堜緥濡備綋妫�鎮h�呮棤棰勭害鏃堕棿銆�
+     * 搴婂ご灞忔秷鎭棤绉戝淇℃伅锛夈�傝嫢鐩存帴浠ユ姤鏂囧唴瀹规暣浣撹鐩栵紝浼氭妸宸叉湁鏁版嵁娓呯┖銆�
+     * 杩欐槸"閮ㄥ垎鏇存柊"璇箟锛屼笌鏇存柊娴佺殑 {@code ifPresent} 閫愬瓧娈佃祴鍊间繚鎸佷竴鑷淬��
+     * <p>
+     * 娉ㄦ剰锛氬湪 {@link AppointmentDO} 灞傚悎骞讹紙鑰岄潪 VO 灞傦級锛屽洜涓�
+     * {@code AppointmentSaveReqVO} 涓� DO 鐨勫瓧娈电被鍨嬪苟涓嶄竴鑷�
+     * 锛圴O 鐨� {@code bookDate/patBirthday} 涓� {@code LocalDateTime}銆�
+     * {@code registerDate/doctorTime} 涓� {@code java.sql.Date}锛�
+     * DO 瀵瑰簲涓� {@code LocalDate} / {@code java.util.Date}锛夈��
+     *
+     * @param target 鏈鎶ユ枃瑙f瀽鍑虹殑瀵硅薄锛堜細琚氨鍦拌ˉ鍏級
+     * @param source 鏁版嵁搴撲腑宸叉湁鐨勮褰�
+     */
+    private void mergeUnsetFields(AppointmentDO target, AppointmentDO source) {
+        if (target.getApplyNo() == null) target.setApplyNo(source.getApplyNo());
+        if (target.getEpisodeId() == null) target.setEpisodeId(source.getEpisodeId());
+        if (target.getPatSrc() == null) target.setPatSrc(source.getPatSrc());
+        if (target.getPatId() == null) target.setPatId(source.getPatId());
+        if (target.getPatName() == null) target.setPatName(source.getPatName());
+        if (target.getPatGender() == null) target.setPatGender(source.getPatGender());
+        if (target.getPatBirthday() == null) target.setPatBirthday(source.getPatBirthday());
+        if (target.getPatMobile() == null) target.setPatMobile(source.getPatMobile());
+        if (target.getPatPhone() == null) target.setPatPhone(source.getPatPhone());
+        if (target.getPatIdentityId() == null) target.setPatIdentityId(source.getPatIdentityId());
+        if (target.getPatAddr() == null) target.setPatAddr(source.getPatAddr());
+        if (target.getPatDeptCode() == null) target.setPatDeptCode(source.getPatDeptCode());
+        if (target.getPatDeptDesc() == null) target.setPatDeptDesc(source.getPatDeptDesc());
+        if (target.getPatWardCode() == null) target.setPatWardCode(source.getPatWardCode());
+        if (target.getPatWardDesc() == null) target.setPatWardDesc(source.getPatWardDesc());
+        if (target.getPatBedNo() == null) target.setPatBedNo(source.getPatBedNo());
+        if (target.getBookDate() == null) target.setBookDate(source.getBookDate());
+        if (target.getBookTimeslot() == null) target.setBookTimeslot(source.getBookTimeslot());
+        if (target.getBookTime() == null) target.setBookTime(source.getBookTime());
+        if (target.getBookCheckType() == null) target.setBookCheckType(source.getBookCheckType());
+        if (target.getBookSrc() == null) target.setBookSrc(source.getBookSrc());
+        if (target.getPaid() == null) target.setPaid(source.getPaid());
+        if (target.getStatus() == null) target.setStatus(source.getStatus());
+        if (target.getRegisterDate() == null) target.setRegisterDate(source.getRegisterDate());
+        if (target.getTolerance() == null) target.setTolerance(source.getTolerance());
+        if (target.getHospitalNo() == null) target.setHospitalNo(source.getHospitalNo());
+        if (target.getMedicalCard() == null) target.setMedicalCard(source.getMedicalCard());
+        if (target.getOutpatientNo() == null) target.setOutpatientNo(source.getOutpatientNo());
+        if (target.getDoctor() == null) target.setDoctor(source.getDoctor());
+        if (target.getDoctorCode() == null) target.setDoctorCode(source.getDoctorCode());
+        if (target.getDoctorTime() == null) target.setDoctorTime(source.getDoctorTime());
+        // 鈥斺�� HIS 琛ュ厖瀛楁锛堥殢琛ㄦ柊澧炲垪涓�璧峰姞鍏ワ紝淇濇寔"閮ㄥ垎鏇存柊涓嶆竻绌�"璇箟锛夆�斺��
+        if (target.getJjbz() == null) target.setJjbz(source.getJjbz());
+        if (target.getMethodCode() == null) target.setMethodCode(source.getMethodCode());
+        if (target.getTargetSiteCode() == null) target.setTargetSiteCode(source.getTargetSiteCode());
+        if (target.getRoomName() == null) target.setRoomName(source.getRoomName());
+        if (target.getRemark() == null) target.setRemark(source.getRemark());
+        if (target.getBookSeqNum() == null) target.setBookSeqNum(source.getBookSeqNum());
+    }
+
+    @Override
+    public void handleAppointmentUpdate(Map<String, Object> dataMap) {
+        if (dataMap == null) {
+            throw exception(APPOINTMENT_UPDATE_FAIL);
+        }
+
+        try {
+            // 浠巇ataMap涓彁鍙栫敵璇峰崟鍙�
+            String applyNo = Optional.ofNullable(dataMap.get("controlActProcess")).map(controlActProcess -> (Map<String, Object>) controlActProcess).map(controlActProcess -> (Map<String, Object>) controlActProcess.get("subject")).map(subject -> (Map<String, Object>) subject.get("observationRequest")).map(observationRequest -> (Map<String, Object>) observationRequest.get("id")).map(id -> (Map<String, Object>) id.get("item")).map(item -> (String) item.get("extension")).orElseThrow(() -> exception(APPOINTMENT_CREATE_FAIL));
+
+            // 鏌ヨ鐜版湁棰勭害璁板綍
+            AppointmentDO existingAppointment = appointmentMapper.getByApplyNo(applyNo);
+
+            // 濡傛灉璁板綍涓嶅瓨鍦紝鍒欒皟鐢╤andleAppointmentCreate杩涜鏂板
+            if (existingAppointment == null) {
+                handleAppointmentCreate(dataMap);
+                return;
+            }
+
+            // 浠巇ataMap涓彁鍙栨暟鎹�
+            Map<String, Object> encounter = extractEncounter(dataMap);
+            if (encounter == null) {
+                throw exception(APPOINTMENT_CREATE_FAIL);
+            }
+
+            // 鏇存柊灏辫瘖娴佹按鍙�
+            Optional.ofNullable(encounter.get("id")).map(id -> (Map<String, Object>) id).map(id -> (List<?>) id.get("item")).filter(items -> items.size() > 1).map(items -> (Map<String, Object>) items.get(1)).map(item -> (String) item.get("extension")).ifPresent(existingAppointment::setEpisodeId);
+
+            // 璁剧疆搴婁綅鍙�
+            Optional.ofNullable(encounter.get("location")).map(location -> (Map<String, Object>) location).map(location -> (Map<String, Object>) location.get("serviceDeliveryLocation")).map(serviceDeliveryLocation -> (Map<String, Object>) serviceDeliveryLocation.get("location")).map(location -> (Map<String, Object>) location.get("id")).map(id -> (Map<String, Object>) id.get("item")).map(item -> (String) item.get("extension")).ifPresent(existingAppointment::setPatBedNo);
+
+
+            // 鏇存柊鎮h�呮潵婧愮被鍨�
+            Optional.ofNullable(encounter.get("code")).map(code -> (Map<String, Object>) code).map(code -> (String) code.get("code")).map(Integer::parseInt).ifPresent(existingAppointment::setPatSrc);
+
+            // 鏇存柊鎮h�呬俊鎭�
+            Optional.ofNullable(encounter.get("subject")).map(subject -> (Map<String, Object>) subject).map(subject -> (Map<String, Object>) subject.get("patient")).map(patient -> (Map<String, Object>) patient.get("patientPerson")).ifPresent(patientPerson -> {
+                // 鏇存柊鎮h�匢D
+                Optional.ofNullable(encounter.get("subject")).map(subject -> (Map<String, Object>) subject).map(subject -> (Map<String, Object>) subject.get("patient")).map(patient -> (Map<String, Object>) patient.get("id")).map(id -> (Map<String, Object>) id).map(id -> (List<?>) id.get("item")).filter(items -> items.size() > 1).map(items -> (Map<String, Object>) items.get(1)).map(item -> (String) item.get("extension")).ifPresent(existingAppointment::setPatId);
+
+                // 鏇存柊鎮h�呭鍚�
+                Optional.ofNullable(patientPerson.get("name")).map(name -> (Map<String, Object>) name).map(name -> (Map<String, Object>) name.get("item")).map(item -> (Map<String, Object>) item.get("part")).map(part -> (String) part.get("value")).ifPresent(existingAppointment::setPatName);
+
+                // 鏇存柊鎮h�呮墜鏈哄拰鐢佃瘽
+                Optional.ofNullable(patientPerson.get("telecom")).map(telecom -> (Map<String, Object>) telecom).map(telecom -> (Map<String, Object>) telecom.get("item")).map(item -> (String) item.get("value")).ifPresent(phone -> {
+                    existingAppointment.setPatMobile(phone);
+                    existingAppointment.setPatPhone(phone);
+                });
+
+                // 鏇存柊鎮h�呮�у埆
+                Optional.ofNullable(patientPerson.get("administrativeGenderCode")).map(genderCode -> (Map<String, Object>) genderCode).map(genderCode -> (String) genderCode.get("code")).map(Byte::parseByte).ifPresent(existingAppointment::setPatGender);
+
+                // 鏇存柊鎮h�呯敓鏃�
+                Optional.ofNullable(patientPerson.get("birthTime")).map(birthTime -> (Map<String, Object>) birthTime).map(birthTime -> (String) birthTime.get("value")).map(birthdayStr -> LocalDate.parse(birthdayStr, DateTimeFormatter.ofPattern("yyyyMMdd"))).ifPresent(existingAppointment::setPatBirthday);
+
+                // 鏇存柊鎮h�呭湴鍧�
+                Optional.ofNullable(patientPerson.get("addr")).map(addr -> (Map<String, Object>) addr).map(addr -> (Map<String, Object>) addr.get("item")).map(item -> (Map<String, Object>) item.get("part")).map(part -> (String) part.get("value")).ifPresent(existingAppointment::setPatAddr);
+
+                // 鏇存柊鎮h�呰韩浠借瘉鍙�
+                Optional.ofNullable(patientPerson.get("id")).map(id -> (Map<String, Object>) id).map(id -> (List<?>) id.get("item")).filter(items -> !items.isEmpty()).map(items -> (Map<String, Object>) items.get(0)).map(item -> (String) item.get("extension")).ifPresent(existingAppointment::setPatIdentityId);
+            });
+
+            // 鏇存柊绉戝鍜岀梾鍖轰俊鎭�
+            Optional.ofNullable(encounter.get("location")).map(location -> (Map<String, Object>) location).map(location -> (Map<String, Object>) location.get("serviceDeliveryLocation")).map(location -> (Map<String, Object>) location.get("serviceProviderOrganization")).ifPresent(org -> {
+                // 鏇存柊绉戝浠g爜鍜屽悕绉�
+                Optional.ofNullable(org.get("id")).map(id -> (Map<String, Object>) id).map(id -> (Map<String, Object>) id.get("item")).map(item -> (String) item.get("extension")).ifPresent(existingAppointment::setPatDeptCode);
+
+                Optional.ofNullable(org.get("name")).map(name -> (Map<String, Object>) name).map(name -> (Map<String, Object>) name.get("item")).map(item -> (Map<String, Object>) item.get("part")).map(part -> (String) part.get("value")).ifPresent(existingAppointment::setPatDeptDesc);
+
+                // 鏇存柊鐥呭尯浠g爜鍜屽悕绉�
+                Optional.ofNullable(org.get("asOrganizationPartOf")).map(wardOrg -> (Map<String, Object>) wardOrg).map(wardOrg -> (Map<String, Object>) wardOrg.get("wholeOrganization")).ifPresent(wardOrg -> {
+                    Optional.ofNullable(wardOrg.get("id")).map(id -> (Map<String, Object>) id).map(id -> (Map<String, Object>) id.get("item")).map(item -> (String) item.get("extension")).ifPresent(existingAppointment::setPatWardCode);
+
+                    Optional.ofNullable(wardOrg.get("name")).map(name -> (Map<String, Object>) name).map(name -> (Map<String, Object>) name.get("item")).map(item -> (Map<String, Object>) item.get("part")).map(part -> (String) part.get("value")).ifPresent(existingAppointment::setPatWardDesc);
+                });
+            });
+
+            // 鏇存柊鏀粯鐘舵��
+            Optional.ofNullable(dataMap.get("controlActProcess")).map(controlActProcess -> (Map<String, Object>) controlActProcess).map(controlActProcess -> (Map<String, Object>) controlActProcess.get("subject")).map(subject -> (Map<String, Object>) subject.get("observationRequest")).map(observationRequest -> (Map<String, Object>) observationRequest.get("zdy")).map(zdy -> {
+                Object payStatus = zdy.get("PayStatus");
+                if (payStatus != null) {
+                    try {
+                        return Integer.parseInt(payStatus.toString());
+                    } catch (NumberFormatException e) {
+                        log.warn("PayStatus杞崲澶辫触: {}", payStatus);
+                        return null;
+                    }
+                }
+                return null;
+            }).ifPresent(existingAppointment::setPaid);
+            // 璁剧疆妫�鏌ョ被鍨�
+            Integer checkTypeValue = resolveBookCheckTypeFromComponent2(dataMap);
+            if (checkTypeValue != null) {
+                existingAppointment.setBookCheckType(checkTypeValue);
+            } else {
+                log.warn("[handleAppointmentUpdate][鏈兘瑙f瀽妫�鏌ョ被鍨嬶紝淇濈暀鍘熷�糫 applyNo={}", applyNo);
+            }
+
+            // 灏咥ppointmentDO杞崲涓篈ppointmentSaveReqVO骞舵洿鏂�
+            AppointmentSaveReqVO updateReqVO = BeanUtils.toBean(existingAppointment, AppointmentSaveReqVO.class);
+            updateAppointment(updateReqVO);
+        } catch (Exception e) {
+            log.error("[handleAppointmentUpdate][鐢宠鍗曟洿鏂板け璐� dataMap({})]", dataMap, e);
+            throw exception(APPOINTMENT_UPDATE_FAIL);
+        }
+    }
+
+    /**
+     * 浠庢姤鏂� {@code component2.observationRequest.code.code} 瑙f瀽鏈湴妫�鏌ョ被鍨嬪�笺��
+     *
+     * <h3>淇鐨勭己闄�</h3>
+     * 鍘熷疄鐜颁负锛�
+     * <pre>
+     *   if (checkType != null || checkType != "")            // 鎭掍负 true锛屼笖閫昏緫搴斾负 &amp;&amp;
+     *       setBookCheckType(Integer.valueOf(
+     *           HisCheckCodeEnum.getByCode(checkType.substring(1))));  // 鏃犻暱搴︽牎楠�
+     * </pre>
+     * 瀛樺湪涓夊闂锛�
+     * <ol>
+     *   <li>{@code ||} 浣垮垽鏂亽鐪燂紱</li>
+     *   <li>{@code substring(1)} 鐩存帴鎴柇锛屾姤鏂囧�奸暱搴︿笉瓒虫椂鎶�
+     *       {@code StringIndexOutOfBoundsException}锛堝疄娴嬫棩蹇椾腑鏈夌被浼煎紓甯革級锛�</li>
+     *   <li>{@code HisCheckCodeEnum.getByCode()} 鍙兘杩斿洖 null锛�
+     *       闅忓悗 {@code Integer.valueOf(null)} 鎶� {@code NumberFormatException}
+     *       锛堝疄娴嬫棩蹇椾腑纭湁 {@code Cannot parse null string}锛夈��</li>
+     * </ol>
+     * 鐜版敼涓猴細HIS 浠g爜鍏煎甯�/涓嶅甫 {@code Y} 鍓嶇紑锛堝搴斿師 substring(1) 璇箟锛夛紝
+     * 涓斾粎鍦ㄨ兘鏄犲皠鍒版湰鍦� {@code check_type} 鍊兼椂鎵嶈繑鍥烇紝鍚﹀垯杩斿洖 null 鐢辫皟鐢ㄦ柟淇濈暀鍘熷�笺��
+     *
+     * @return 鏈湴妫�鏌ョ被鍨嬪�硷紙濡� 1000锛夛紱鏃犳硶瑙f瀽鏃惰繑鍥� null
+     */
+    private Integer resolveBookCheckTypeFromComponent2(Map<String, Object> dataMap) {
+        // 绫诲瀷瀹夊叏锛歰bservationRequest.code 鍙兘鏄� Map锛屼篃鍙兘鏄┖瀛楃涓�
+        String hisCode = digText(dataMap, "controlActProcess", "subject", "observationRequest",
+                "component2", "observationRequest", "code", "code");
+        if (StringUtils.isEmpty(hisCode)) {
+            return null;
+        }
+        String raw = hisCode.trim().replaceAll("\\s+", "");
+        // 鍏煎锛欻IS 鎶ユ枃鍊煎舰濡� "Y1202065"锛岃�屾灇涓� code 涓哄幓鎺夐瀛楃鍚庣殑 "1202065"锛�
+        // 鍚屾椂涔熷蹇嶆姤鏂囨湰韬笉甯﹀墠缂�
+        String[] candidates = raw.length() > 1
+                ? new String[]{raw, raw.substring(1)}
+                : new String[]{raw};
+        for (String candidate : candidates) {
+            String mapped = HisCheckCodeEnum.getByCode(candidate);
+            if (StringUtils.isNotEmpty(mapped)) {
+                return parseIntegerQuietly(mapped);
+            }
+        }
+        log.warn("[resolveBookCheckTypeFromComponent2][HIS 妫�鏌ヤ唬鐮佹棤娉曟槧灏勫埌鏈湴妫�鏌ョ被鍨媇 hisCode={}", hisCode);
+        return null;
+    }
+
+    /**
+     * 鐢宠鍗曠姸鎬佹洿鏂帮紙action: {@code S0405} / {@code S0405HIS} / {@code S040501HIS}锛夈��
+     *
+     * <h3>鈿狅笍 璇ユ湇鍔℃槸鍏ㄩ櫌閫氱敤鐨�</h3>
+     * 鎺ュ彛鏂囨。 6.3 瀹氫箟鍏� action 涓� {@code S0405|S0405HIS|S040501HIS}锛�**涓嶅尯鍒嗙瀹�**銆�
+     * 鍖婚櫌浼氭妸鎵�鏈夋鏌ョ瀹わ紙CT銆丮RI銆丏R銆佽秴澹般�侀瀵嗗害鈥︼級鐨勬鏌ョ姸鎬佷竴骞舵帹閫併��
+     * 瀹炴祴 2026-09-17锛氭湰鏈嶅姟鏀跺埌 1225 娆¤姹傦紝鍏朵腑浠� 3.5% 鐨勭敵璇峰崟鍙峰睘蹇冪數涓氬姟锛�
+     * 鍏朵綑涓烘斁灏�/瓒呭0绛夌瀹ょ殑鍗曪紙濡� {@code 2126091700001} 鑳搁儴CT骞虫壂 / 鎬ヨ瘖CT瀹わ級銆�
+     *
+     * <h3>澶勭悊绛栫暐</h3>
+     * <ol>
+     *   <li>鎻愬彇鐢宠鍗曞彿涓庢鏌ョ姸鎬佺爜锛屽苟瀵圭姸鎬佺爜鍋�**褰掍竴鍖�**锛堝幓鍓嶅闆躲�佹牎楠屽悎娉曟�э級</li>
+     *   <li>鏌ユ湰鍦伴绾﹁褰曪細
+     *     <ul>
+     *       <li><b>瀛樺湪</b> 鈫� 鏇存柊鐘舵�侊紱鐘舵�佷负 3锛堟鏌ョ櫥璁帮級鏃跺悓鏃跺啓鍏ョ櫥璁版椂闂�</li>
+     *       <li><b>涓嶅瓨鍦� + 鏄績鐢靛崟</b> 鈫� 璁� WARN 骞�**鎻掑叆鏂拌褰�**锛圦4 瑕佹眰瀹归敊鏂板锛�</li>
+     *       <li><b>涓嶅瓨鍦� + 闈炲績鐢靛崟</b> 鈫� 浠呰 DEBUG 鏃ュ織锛�**涓嶆彃鍏�**锛堥伩鍏嶇亴鍏ユ棤鍏虫暟鎹級</li>
+     *     </ul>
+     *   </li>
+     * </ol>
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void handleAppointmentStateUpdate(Map<String, Object> dataMap) {
+        if (dataMap == null) {
+            throw exception(APPOINTMENT_STATUS_UPDATE_FAIL);
+        }
+
+        // 1. 鎻愬彇鐢宠鍗曞彿锛堢被鍨嬪畨鍏ㄥ鑸級
+        String applyNo = digText(dataMap, "controlActProcess", "subject", "placerGroup",
+                "component2", "observationRequest", "id", "item", "extension");
+        if (StringUtils.isEmpty(applyNo)) {
+            log.warn("[handleAppointmentStateUpdate][鎶ユ枃缂哄皯鐢宠鍗曞彿锛屽拷鐣 dataMap={}", dataMap);
+            throw exception(HIS_APPLY_NO_MISSING);
+        }
+
+        // 2. 鎻愬彇骞跺綊涓�鍖栨鏌ョ姸鎬佺爜
+        //    绫诲瀷瀹夊叏锛歱rocessStep.code 鍦ㄩ儴鍒嗘姤鏂囦腑鏄� Map锛坈ode={code=7,...}锛夛紝
+        //    鍦ㄥ彟涓�浜涙姤鏂囦腑鍙兘鏄┖瀛楃涓诧紝鐩存帴鐢ㄩ摼寮忓己杞細鎶� ClassCastException
+        String rawStatus = digText(dataMap, "controlActProcess", "subject", "placerGroup",
+                "component2", "observationRequest", "component1", "processStep", "code", "code");
+        String status = HisCheckStatusEnum.normalize(rawStatus);
+        if (status == null) {
+            // 瀹炴祴鎶ユ枃涓鍊煎瓨鍦� "3" / "03" / "7" / "08" / "10" 绛変笉鍚屽啓娉曪紝
+            // 褰掍竴鍖栧悗浠嶆棤娉曡瘑鍒鏄庝笉鏄紪鐮佽〃鍐呯殑鐘舵�侊紙濡傜梾鐞嗕笓鐢� 21/22/23锛�
+            log.warn("[handleAppointmentStateUpdate][妫�鏌ョ姸鎬佺紪鐮侀潪娉曟垨涓嶅湪缂栫爜琛ㄥ唴锛屽拷鐣 applyNo={} rawStatus={}",
+                    applyNo, rawStatus);
+            throw exception(HIS_STATUS_INVALID);
+        }
+
+        // 3. 鏈湴璁板綍鍖归厤
+        AppointmentDO existingAppointment = appointmentMapper.getByApplyNo(applyNo);
+        if (existingAppointment != null) {
+            existingAppointment.setStatus(status);
+            if (HisCheckStatusEnum.REGISTERED.getCode().toString().equals(status)) {
+                existingAppointment.setRegisterDate(new Date());
+            }
+            AppointmentSaveReqVO updateReqVO = BeanUtils.toBean(existingAppointment, AppointmentSaveReqVO.class);
+            updateAppointment(updateReqVO);
+            return;
+        }
+
+        // 4. 璁板綍涓嶅瓨鍦細鍏堝垽瀹氭槸鍚﹀績鐢典笟鍔″崟锛岄潪蹇冪數鍗曞彧璁版棩蹇椼�佷笉鎻掑叆
+        //
+        //    S0405 鏄叏闄㈤�氱敤娴併�傚彲鐢ㄧ殑蹇冪數鍒ゅ畾渚濇嵁锛堟寜瀹炴祴鎶ユ枃锛夛細
+        //      A. apply_no 鍚庣紑锛�1101/6401锛�
+        //      B. component2.observationRequest.component1.processStep.code.ZDY
+        //         涓殑 jcxmcode / jcxmname锛堝疄娴� 1138/1225 鏉″甫璇ュ瓧娈碉紝
+        //         褰㈠ jcxmname=鑳搁儴CT骞虫壂,涓婅吂閮–T骞虫壂 / roomname=鎬ヨ瘖CT瀹わ級
+        //      C. 鎵ц绉戝锛堥渶閰嶇疆鐧藉悕鍗曪級
+        Map<String, Object> zdyFromS0405 = extractZdyFromStateMessage(dataMap);
+        String s0405CheckCode = extractZdyValueFromMap(zdyFromS0405, "jcxmcode");
+        String s0405CheckName = extractZdyValueFromMap(zdyFromS0405, "jcxmname");
+        String hisDeptCode = extractDeptCodeFromComponent2(dataMap);
+        if (!hisEcgFilter.isEcgOrder("S0405", applyNo, s0405CheckCode, s0405CheckName, hisDeptCode)) {
+            // 鍏ㄩ櫌娴佷腑鐨勫叾浠栫瀹ゅ崟鎹紝闈欓粯璺宠繃锛圖EBUG 鏃ュ織鐢� filter 杈撳嚭锛�
+            return;
+        }
+
+        // 5. 鏄績鐢靛崟浣嗘湰鍦版棤璁板綍 鈫� 瀹归敊鏂板锛圦4 瑕佹眰锛�
+        log.warn("[handleAppointmentStateUpdate][蹇冪數涓氬姟鍗曚絾鏈湴鏃犺褰曪紝鎵ц瀹归敊鏂板] applyNo={} status={} dept={}",
+                applyNo, status, hisDeptCode);
+        insertAppointmentFromStateMessage(applyNo, status, dataMap);
+    }
+
+    /**
+     * 浠� S0405 鎶ユ枃涓瀯閫犲苟鎻掑叆涓�鏉¢绾﹁褰曘��
+     *
+     * <h3>鈿狅笍 宸茬煡鐨勬暟鎹己澶�</h3>
+     * {@code S0405} 鎶ユ枃**涓嶅寘鍚鏌ラ」鐩俊鎭�**锛堟棤 {@code component2.observationRequest.code}锛夛紝
+     * 鍥犳鏃犳硶纭畾 {@code book_check_type}锛堟鏌ョ被鍨嬶級銆傚疄娴嬬‘璁わ細鐢宠鍗曞彿鍚庣紑
+     * 涓庢鏌ョ被鍨�**鏃犲搴斿叧绯�**锛堝悓涓� {@code ...1101} 鍚庣紑鐨勮褰曞瓨鍦� 1000/100/1100 绛夊绉嶇被鍨嬶級锛�
+     * 鏁呬笉鍋氱寽娴嬶紝淇濇寔涓虹┖骞惰緭鍑� WARN锛屽緟鍚庣画 {@code S0202ECG}锛堢敵璇峰崟鏇存柊锛�
+     * 鎴栦汉宸ヨˉ褰曚慨姝c��
+     * <p>
+     * 鍚岀悊 {@code book_date}锛堥绾︽棩鏈燂級涔熶笉鍦ㄦ姤鏂囦腑锛屼繚鎸佷负绌� 鈥斺�� 杩欎笌鐜版湁鏁版嵁涓�鑷�
+     * 锛堝簱涓害 48% 璁板綍 {@code book_date} 涓虹┖锛岄绾︽棩鏈熺敱 S050401/S040501HIS 鍚庣画琛ュ叆锛夈��
+     */
+    private void insertAppointmentFromStateMessage(String applyNo, String status, Map<String, Object> dataMap) {
+        AppointmentDO appointment = new AppointmentDO();
+        appointment.setApplyNo(applyNo);
+        appointment.setStatus(status);
+        if (HisCheckStatusEnum.REGISTERED.getCode().toString().equals(status)) {
+            appointment.setRegisterDate(new Date());
+        }
+
+        // 鎮h�呬俊鎭紙S0405 鐨� patient.id.item 涓哄垪琛細鍩烮D/鎮h�匢D/闂ㄨ瘖鍙�/浣忛櫌鍙�/灏辫瘖娆℃暟/灏辫瘖娴佹按鍙凤級
+        // 鐢ㄧ被鍨嬪畨鍏ㄥ鑸紝閬垮厤鏌愪竴绾т笉鏄� Map 鏃舵姏 ClassCastException
+        Map<String, Object> encounter = dig(dataMap, "controlActProcess", "subject",
+                "placerGroup", "componentOf1", "encounter");
+        if (encounter != null) {
+            // 灏辫瘖娴佹按鍙凤細id.item[1]
+            List<?> encIdItems = asList(dig(encounter, "id"), "item");
+            if (encIdItems.size() > 1 && encIdItems.get(1) instanceof Map) {
+                appointment.setEpisodeId(str(((Map<String, Object>) encIdItems.get(1)).get("extension")));
+            }
+            // 鎮h�呬俊鎭�
+            Map<String, Object> patient = dig(encounter, "subject", "patient");
+            if (patient != null) {
+                List<?> patIdItems = asList(patient, "id", "item");
+                // 鎮h�匢D item[1]锛岄棬璇婂彿 item[2]锛屼綇闄㈠彿 item[3]
+                if (patIdItems.size() > 1 && patIdItems.get(1) instanceof Map) {
+                    appointment.setPatId(str(((Map<String, Object>) patIdItems.get(1)).get("extension")));
+                }
+                if (patIdItems.size() > 2 && patIdItems.get(2) instanceof Map) {
+                    appointment.setOutpatientNo(str(((Map<String, Object>) patIdItems.get(2)).get("extension")));
+                }
+                if (patIdItems.size() > 3 && patIdItems.get(3) instanceof Map) {
+                    appointment.setHospitalNo(str(((Map<String, Object>) patIdItems.get(3)).get("extension")));
+                }
+                // 濮撳悕
+                String patName = digText(patient, "patientPerson", "name", "item", "part", "value");
+                if (StringUtils.isNotEmpty(patName)) {
+                    appointment.setPatName(patName);
+                }
+                // 鎬у埆锛歱at_gender 涓� NOT NULL锛屽敖閲忓彇锛涘彇涓嶅埌鏃剁敱涓嬫柟鍏滃簳涓� 0
+                String gender = digText(patient, "patientPerson", "administrativeGenderCode", "code");
+                Byte genderByte = parseByteQuietly(gender);
+                if (genderByte != null) {
+                    appointment.setPatGender(genderByte);
+                }
+            }
+        }
+
+        // 鎵ц绉戝锛堝啓鍏� pat_dept_code / pat_dept_desc锛�
+        Map<String, Object> org = dig(dataMap, "controlActProcess", "subject", "placerGroup",
+                "component2", "observationRequest", "location", "serviceDeliveryLocation",
+                "serviceProviderOrganization");
+        if (org != null) {
+            String deptCode = digText(org, "id", "item", "extension");
+            if (StringUtils.isNotEmpty(deptCode)) {
+                appointment.setPatDeptCode(deptCode);
+            }
+            String deptDesc = digText(org, "name", "item", "part", "value");
+            if (StringUtils.isNotEmpty(deptDesc)) {
+                appointment.setPatDeptDesc(deptDesc);
+            }
+        }
+
+        if (StringUtils.isEmpty(appointment.getPatName())) {
+            log.warn("[insertAppointmentFromStateMessage][瀹归敊鏂板鐨勮褰曠己灏戞偅鑰呭鍚峕 applyNo={}", applyNo);
+        }
+        log.warn("[insertAppointmentFromStateMessage][娉ㄦ剰锛歋0405 鎶ユ枃涓嶅惈妫�鏌ラ」鐩紝" +
+                "book_check_type 灏嗕负绌猴紝闇�鍚庣画 S0202ECG 鎴栦汉宸ヨˉ褰昡 applyNo={}", applyNo);
+        // pat_id / pat_name / pat_gender 鍦ㄦ暟鎹簱涓潎涓� NOT NULL锛岀己鍊兼椂鎻愬墠鎶ラ敊锛�
+        // 閬垮厤 SQL 寮傚父瀵艰嚧浜嬪姟鍥炴粴锛堝疄娴嬫姤閿欙細Field 'pat_gender' doesn't have a default value锛�
+        if (StringUtils.isEmpty(appointment.getPatId())) {
+            log.error("[insertAppointmentFromStateMessage][鎶ユ枃缂烘偅鑰呯紪鍙�(pat_id)锛屾棤娉曟柊澧瀅 applyNo={}", applyNo);
+            throw exception(PATIENT_NOT_EXISTS);
+        }
+        if (appointment.getPatGender() == null) {
+            log.warn("[insertAppointmentFromStateMessage][鎶ユ枃鏈彁渚涙偅鑰呮�у埆锛屾寜鏈煡(0)鍐欏叆] applyNo={}", applyNo);
+            appointment.setPatGender(UNKNOWN_GENDER);
+        }
+
+        AppointmentSaveReqVO saveReqVO = BeanUtils.toBean(appointment, AppointmentSaveReqVO.class);
+        createAppointment(saveReqVO);
+    }
+
+    /**
+     * 鍙� S0405 鎶ユ枃涓殑 {@code ZDY} 鑺傜偣銆�
+     * <p>
+     * 瀹炴祴璺緞锛歿@code controlActProcess.subject.placerGroup.component2.observationRequest
+     * .component1.processStep.code.ZDY}锛屽唴瀹瑰舰濡傦細
+     * <pre>
+     *   ZDY={roomcode=111, roomname=鎬ヨ瘖CT瀹�, jcxmcode=1080,3016,3014,
+     *        jcxmname=鑳搁儴CT骞虫壂,涓婅吂閮–T骞虫壂,涓嬭吂閮–T骞虫壂,
+     *        pdh=000336, yysd=00:00-01:00, isjj=1}
+     * </pre>
+     * 娉ㄦ剰锛歿@code observationRequest.code} 鍦ㄦ姤鏂囦腑**鍙兘鏄┖瀛楃涓茶�岄潪 Map**锛�
+     * 鏁呭繀椤荤敤 {@link #dig(Object, String...)} 鍋氱被鍨嬪畨鍏ㄥ鑸��
+     */
+    private Map<String, Object> extractZdyFromStateMessage(Map<String, Object> dataMap) {
+        return dig(dataMap, "controlActProcess", "subject", "placerGroup", "component2",
+                "observationRequest", "component1", "processStep", "code", "ZDY");
+    }
+
+    /** 浠� Map 涓彇瀛楃涓插瓧娈碉紙鍙负绌猴級 */
+    private String extractZdyValueFromMap(Map<String, Object> map, String key) {
+        if (map == null) {
+            return null;
+        }
+        Object v = map.get(key);
+        return v == null ? null : v.toString();
+    }
+
+    /** 瀹夊叏杞瓧绗︿覆锛歯ull 杩斿洖 null锛屽叾浣� toString() */
+    private static String str(Object o) {
+        return o == null ? null : o.toString();
+    }
+
+    /** {@code appointment.remark} 鍒楀涓婇檺锛堣 08_fix_remark_length.sql锛屽綋鍓� varchar(2000)锛� */
+    private static final int REMARK_MAX_LEN = 2000;
+    /** {@code appointment.room_name} 鍒楀涓婇檺 */
+    private static final int ROOM_NAME_MAX_LEN = 200;
+
+    /**
+     * 鎸夊垪瀹芥埅鏂枃鏈紝闃叉 {@code Data too long for column} 瀵艰嚧**鏁翠釜浜嬪姟鍥炴粴**銆�
+     *
+     * <p><b>琛�娉暀璁�</b>锛�2026-09-28 鐢熶骇鐜璇ラ敊璇嚭鐜� 52 娆°�傚績鐢靛浘杩愬姩璇曢獙鐨�
+     * 娉ㄦ剰浜嬮」闀胯揪 474 瀛楃锛岃�� {@code remark} 鍒楀師涓� {@code varchar(255)}锛�
+     * 婧㈠嚭鍚庢姏 {@code MysqlDataTruncation}锛屼娇鏁存潯 UPDATE 鍥炴粴 鈥斺��
+     * 杩炲甫鍚屼竴浜嬪姟涓殑 {@code book_time}銆亄@code room_name} 涓�璧蜂涪澶憋紝
+     * 琛ㄧ幇涓�"棰勭害鏃堕棿娈垫病瀛樿繘鏁版嵁搴�"銆�
+     *
+     * @param value 鍘熷鏂囨湰
+     * @param maxLen 鐩爣鍒楀
+     * @return 鎴柇鍚庣殑鏂囨湰锛坣ull 鍘熸牱杩斿洖锛�
+     */
+    private static String truncate(String value, int maxLen) {
+        if (value == null) {
+            return null;
+        }
+        if (value.length() <= maxLen) {
+            return value;
+        }
+        log.warn("[truncate][鏂囨湰瓒呴暱宸叉埅鏂璢 鍘熼暱搴�={} 鎴柇鑷�={} 鍐呭鍓� 60 瀛�={}",
+                value.length(), maxLen,
+                value.substring(0, Math.min(60, value.length())));
+        return value.substring(0, maxLen);
+    }
+
+    /**
+     * 灞傜骇瀵艰埅鍚庡彇銆屽垪琛ㄣ�嶈妭鐐癸細{@code dig(root, path...)} 寰楀埌鐨勫�艰嫢鏄� List 鍒欒繑鍥烇紝
+     * 鍚﹀垯杩斿洖绌哄垪琛紙**涓嶆姏寮傚父**锛夈��
+     */
+    @SuppressWarnings("unchecked")
+    private static List<?> asList(Map<String, Object> root, String... path) {
+        Object cur = root;
+        for (String key : path) {
+            if (!(cur instanceof Map)) {
+                return Collections.emptyList();
+            }
+            cur = ((Map<String, Object>) cur).get(key);
+        }
+        return cur instanceof List ? (List<?>) cur : Collections.emptyList();
+    }
+
+    /**
+     * 绫诲瀷瀹夊叏鐨勫眰绾у鑸細閫愮骇鍙� Map 瀛愯妭鐐广��
+     *
+     * <p><b>涓轰粈涔堥渶瑕佸畠</b>锛欻IS 鎶ユ枃缁� Jackson 鍙嶅簭鍒楀寲鍚庯紝鍚屼竴瀛楁鐨勭被鍨�**骞朵笉绋冲畾**銆�
+     * 渚嬪 {@code observationRequest.code} 鍦ㄩ儴鍒嗘姤鏂囦腑鏄� {@code Map}锛坽@code code={code=7,...}}锛夛紝
+     * 鍦ㄥ彟涓�浜涙姤鏂囦腑鏄�**绌哄瓧绗︿覆**锛坽@code code=}锛夈�傝嫢鐢�
+     * {@code .map(m -> (Map<String,Object>) m.get("code"))} 杩欑閾惧紡寮哄埗杞崲锛�
+     * 閬囧埌瀛楃涓蹭細鎶� {@code ClassCastException}锛�**瀵艰嚧鏁存潯娑堟伅澶勭悊涓柇**銆�
+     *
+     * <p>瀹炴祴鐢熶骇鏃ュ織 2026-09-28 涓绫诲紓甯稿嚭鐜� **287 娆�**锛屾鏄敱姝ゅ啓娉曞紩璧�
+     * 锛坽@code AppointmentServiceImpl.lambda$extractCheckCodeFromComponent2}锛夈��
+     *
+     * @param root 璧峰鑺傜偣
+     * @param path 渚濇瑕佸彇鐨勯敭
+     * @return 鏈鑺傜偣锛涗换涓�鐜妭涓嶆槸 Map 鎴栫己澶辨椂杩斿洖 null锛�**涓嶆姏寮傚父**锛�
+     */
+    @SuppressWarnings("unchecked")
+    private static Map<String, Object> dig(Map<String, Object> root, String... path) {
+        Object cur = root;
+        for (String key : path) {
+            if (!(cur instanceof Map)) {
+                return null;
+            }
+            cur = ((Map<String, Object>) cur).get(key);
+        }
+        return cur instanceof Map ? (Map<String, Object>) cur : null;
+    }
+
+    /** HIS 鎶ユ枃涓�屽尰鍢卞彿/鍖诲槺ID銆嶇殑 id root锛堣銆婁氦浜掓湇鍔¢�氱敤鎺ュ彛鏂囨。銆�6.1 / 6.4 / 6.5锛� */
+    private static final String ORDER_NO_ROOT = "2.16.156.10011.1.28";
+
+    /**
+     * 鍦� HL7 鐨� {@code id} 鑺傜偣涓紝鎸� {@code root} 鍖归厤骞跺彇鍑� {@code extension}銆�
+     * <p>
+     * 鍏煎涓夌鐪熷疄褰㈡�侊紙鍧囧凡鍦ㄦ姤鏂�/鏂囨。涓嚭鐜帮級锛�
+     * <pre>
+     *   id={item={root=..., extension=...}}          // 鍗曚釜 Map
+     *   id={item=[{root=..., extension=...}, {...}]} // List锛堝鏍囪瘑锛岄』鎸� root 鍖哄垎锛�
+     *   {root=..., extension=...}                    // 鐩存帴灏辨槸鏍囪瘑鏈韩
+     * </pre>
+     * <p>
+     * <b>涓轰粈涔堜笉澶嶇敤 {@link #digText}</b>锛歿@code digText} 閬囧埌 List 鍙彇棣栧厓绱狅紝
+     * 鑰� {@code id.item} 涓烘暟缁勬椂鍙兘棣栧厓绱犳槸銆岀敵璇峰崟鍙�(root 1.24)銆嶈�岄潪銆屽尰鍢卞彿(root 1.28)銆嶏紝
+     * 浼氬彇閿欏�笺�傛晠杩欓噷鏄惧紡閬嶅巻骞舵寜 root 鍖归厤銆�
+     *
+     * @param idNode {@code id} 鑺傜偣锛圡ap 鎴� List锛屽彲涓� null锛�
+     * @param root   鐩爣 root 鍊�
+     * @return 鍖归厤椤圭殑 extension锛涙湭鍖归厤/涓虹┖鏃惰繑鍥� null
+     */
+    private static String findExtensionByRoot(Object idNode, String root) {
+        if (idNode == null) {
+            return null;
+        }
+        Object item = (idNode instanceof Map) ? ((Map<String, Object>) idNode).get("item") : idNode;
+        if (item == null) {
+            return null;
+        }
+        List<Object> candidates = new ArrayList<>();
+        if (item instanceof List) {
+            candidates.addAll((List<Object>) item);
+        } else {
+            candidates.add(item);
+        }
+        for (Object cand : candidates) {
+            if (!(cand instanceof Map)) {
+                continue;
+            }
+            Map<String, Object> m = (Map<String, Object>) cand;
+            Object r = m.get("root");
+            if (r != null && root.equals(r.toString().trim())) {
+                Object ext = m.get("extension");
+                if (ext != null && StringUtils.isNotEmpty(ext.toString().trim())) {
+                    return ext.toString().trim();
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 浠� HIS 鎶ユ枃涓彇 {@code zdy} 鑷畾涔夊瓧娈靛尯鐨勩�屾槸鍚﹀簥杈广�嶆爣璁帮紙闇�姹�2锛夈��
+     *
+     * <h4>瀛楁鏉ユ簮</h4>
+     * {@code controlActProcess.subject.observationRequest.zdy.ISCB}锛�
+     * 鍙栧�� {@code 0=鍚� 1=鏄瘆銆�
+     *
+     * <p>璇ュ瓧娈�**鍦� HIS 瀹樻柟銆婁氦浜掓湇鍔¢�氱敤鎺ュ彛鏂囨。銆嬩腑鏈畾涔�** 鈥斺�� 鏂囨。閲� 25 涓�
+     * {@code zdy} 鍧楀叡 19 涓瓧娈碉紝瀹炴祴鐪熷疄鎶ユ枃鍗存湁 26 涓紝{@code ISCB} 灞炲叾涓�
+     * 7 涓湭鍏紑鎵╁睍瀛楁涔嬩竴锛坽@code ISCB/ISDN/BLLSH/SWCJBS/KDYSSJH/KLX/VTEGWBZ}锛夈��
+     * 瀹炴祴鏍锋湰涓� {@code ISCB} 鎭掍负 0銆�
+     *
+     * <h4>涓庛�屽簥杈规鏌ョ被鍨嬨�嶇殑鍖哄埆</h4>
+     * {@code check_type.value=1000} 鐨勫悕绉版槸"搴婅竟蹇冪數鍥�(甯歌+棰戣氨)"锛岄偅鏄�**涓�涓鏌ラ」鐩�**锛�
+     * 鏈爣璁拌〃杈剧殑鏄�**鍚屼竴椤圭洰銆佹偅鑰呴渶鍒板簥杈瑰仛**锛屼袱鑰呰涔変笉鍚岋紝涓嶅彲浜掔浉鏇夸唬銆�
+     *
+     * <p>鎶芥垚闈欐�佹柟娉曟槸涓轰簡鍙崟鍏冩祴璇曪紙瑙� {@code HisParseSafetyTest}锛夈��
+     *
+     * @param dataMap HIS 鎶ユ枃鏍硅妭鐐�
+     * @return {@code "0"}/{@code "1"}锛涘瓧娈电己澶辨垨涓虹┖鏃惰繑鍥� null
+     */
+    static String findBedsideFlag(Map<String, Object> dataMap) {
+        if (dataMap == null) {
+            return null;
+        }
+        Object cap = dataMap.get("controlActProcess");
+        if (!(cap instanceof Map)) {
+            return null;
+        }
+        Object subject = ((Map<String, Object>) cap).get("subject");
+        if (!(subject instanceof Map)) {
+            return null;
+        }
+        Object obs = ((Map<String, Object>) subject).get("observationRequest");
+        if (!(obs instanceof Map)) {
+            return null;
+        }
+        Object zdy = ((Map<String, Object>) obs).get("zdy");
+        if (!(zdy instanceof Map)) {
+            return null;
+        }
+        Object iscb = ((Map<String, Object>) zdy).get("ISCB");
+        if (iscb == null) {
+            return null;
+        }
+        String v = iscb.toString().trim();
+        return v.isEmpty() ? null : v;
+    }
+
+    /**
+     * 鍙栨湯绔妭鐐圭殑鏍囬噺鍊硷紙鐢ㄤ簬 {@code code} 鍙兘鏄� Map 涔熷彲鑳芥槸瀛楃涓茬殑鍦烘櫙锛夈��
+     *
+     * @return 鏍囬噺鏂囨湰锛涙湯绔负 Map 鏃跺彇鍏朵腑鐨� {@code value}/{@code code}锛涘彇涓嶅埌杩斿洖 null
+     */
+    private static String digText(Map<String, Object> root, String... path) {
+        Object cur = root;
+        for (int i = 0; i < path.length; i++) {
+            if (cur instanceof Map) {
+                cur = ((Map<String, Object>) cur).get(path[i]);
+            } else if (cur instanceof List) {
+                // 鍒楄〃褰㈡�侊紙濡� item=[{...}]锛夊彇绗竴涓厓绱犵户缁�
+                List<?> list = (List<?>) cur;
+                if (list.isEmpty() || !(list.get(0) instanceof Map)) {
+                    return null;
+                }
+                cur = ((Map<String, Object>) list.get(0)).get(path[i]);
+            } else {
+                return null;
+            }
+        }
+        if (cur == null) {
+            return null;
+        }
+        if (cur instanceof Map) {
+            Map<String, Object> m = (Map<String, Object>) cur;
+            Object v = m.get("value");
+            if (v == null) {
+                v = m.get("code");
+            }
+            return v == null ? null : v.toString();
+        }
+        if (cur instanceof String) {
+            String s = (String) cur;
+            return s.trim().isEmpty() ? null : s;
+        }
+        return cur.toString();
+    }
+
+    /** 浠庢姤鏂囩殑 {@code component2.observationRequest.code.code} 鍙栨鏌ラ」鐩唬鐮侊紙鍙负绌猴級 */
+    private String extractCheckCodeFromComponent2(Map<String, Object> dataMap) {
+        // 绫诲瀷瀹夊叏锛歝ode 鍙兘鏄� Map锛屼篃鍙兘鏄┖瀛楃涓�
+        return digText(dataMap, "controlActProcess", "subject", "placerGroup", "component2",
+                "observationRequest", "code", "code");
+    }
+
+    /** 浠庢姤鏂囩殑 {@code component2.observationRequest.location...} 鍙栨墽琛岀瀹ょ紪鐮侊紙鍙负绌猴級 */
+    private String extractDeptCodeFromComponent2(Map<String, Object> dataMap) {
+        return digText(dataMap, "controlActProcess", "subject", "placerGroup", "component2",
+                "observationRequest", "location", "serviceDeliveryLocation",
+                "serviceProviderOrganization", "id", "item", "extension");
+    }
+
+    /**
+     * 浠� dataMap 涓彁鍙� encounter 鏁版嵁銆�
+     *
+     * <p>鎶ユ枃璺緞锛歿@code controlActProcess.subject.observationRequest.componentOf1.encounter}銆�
+     *
+     * <p><b>娉ㄦ剰</b>锛歿@code componentOf1} 浣嶄簬 {@code observationRequest} 涔嬩笅锛�
+     * 涓� {@code id}銆亄@code code}銆亄@code component2} 鍚岀骇锛�**涓嶆槸** {@code subject} 鐨勭洿鎺ュ瓙鑺傜偣銆�
+     * 鏋勯�犳祴璇曟姤鏂囨垨瑙f瀽鏃ュ織鏃跺鏄撴悶閿欏眰绾э紝姝ゅ閫愮骇鍒ょ┖浠ヤ究瀹氫綅銆�
+     */
+    @SuppressWarnings("unchecked")
+    private Map<String, Object> extractEncounter(Map<String, Object> dataMap) {
+        Map<String, Object> cap = (Map<String, Object>) dataMap.get("controlActProcess");
+        if (cap == null) {
+            return null;
+        }
+        Map<String, Object> subject = (Map<String, Object>) cap.get("subject");
+        if (subject == null) {
+            return null;
+        }
+        Map<String, Object> observationRequest = (Map<String, Object>) subject.get("observationRequest");
+        if (observationRequest == null) {
+            return null;
+        }
+        Object componentOf1 = observationRequest.get("componentOf1");
+        if (componentOf1 == null) {
+            return null;
+        }
+        return (Map<String, Object>) ((Map<String, Object>) componentOf1).get("encounter");
+    }
+
+    /**
+     * 妫�鏌ラ绾︾姸鎬佹柊澧�/鏇存柊
+     * 锛坅ction: {@code S050401}/{@code S050402} 鏂板銆亄@code S050501}/{@code S050502} 鏇存柊锛夈��
+     *
+     * <h3>鏈鏀瑰姩</h3>
+     * <ol>
+     *   <li><b>鍒犻櫎姝讳唬鐮�</b>锛氬師瀹炵幇鍦ㄨ褰曚笉瀛樺湪鏃惰皟鐢� {@code handleAppointmentCreate(dataMap)}锛�
+     *       浣嗘湰鏈嶅姟鐨勬姤鏂囩粨鏋勬槸 {@code subject.actAppointment}锛岃�� create 鏈熸湜
+     *       {@code subject.observationRequest} 鈥斺�� 涓よ�呭畬鍏ㄤ笉鍏煎锛寋@code extractEncounter}
+     *       蹇呯劧杩斿洖 null 骞舵姏寮傚父锛涘師绱ч殢鍏跺悗鐨� {@code log.warn("鏈壘鍒伴绾﹁褰�")}
+     *       鍥犳**姘歌繙涓嶄細鎵ц**銆傜幇鏀逛负鏄庣‘璁版棩蹇� + 瀹归敊鏂板銆�</li>
+     *   <li><b>鐘舵�佸綊涓�鍖�</b>锛歿@code ZDY.statusCode} 鏄�**棰勭害鐘舵��**锛堝 1=宸查绾︼級锛�
+     *       涓� {@code S0405} 鐨�**妫�鏌ョ姸鎬佺紪鐮�**璇箟涓嶅悓锛屼絾鍚屽啓鍏� {@code status} 鍒椼��
+     *       杩欓噷缁熶竴璧� {@link HisCheckStatusEnum#normalize(String)} 鏍¢獙銆�</li>
+     *   <li><b>瀹归敊鏂板</b>锛圦4 瑕佹眰锛夛細璁板綍涓嶅瓨鍦ㄦ椂璁� WARN 骞朵緷鎹湰鎶ユ枃鎻掑叆鏂拌褰曘��</li>
+     *   <li><b>鍚敤鏈娇鐢ㄥ瓧娈�</b>锛圦7 瑕佹眰锛夛細{@code ZDY.remark}锛堟敞鎰忎簨椤癸級銆�
+     *       {@code ZDY.area}锛堜綅缃級銆亄@code ZDY.roomcode}/{@code roomname}锛堟埧闂达級銆�
+     *       {@code patient.id.item[4]}锛堥绾︽帓搴忓彿锛夈��</li>
+     *   <li><b>绌哄�奸槻寰�</b>锛氬師瀹炵幇鐩存帴 {@code LocalDateTime.parse(bookTimeStr)}
+     *       涓� {@code Integer.parseInt(bookSrc)}锛屾姤鏂囩己瀛楁鏃朵細鎶涘紓甯稿鑷存暣鏉℃秷鎭け璐ャ��</li>
+     * </ol>
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void handleCheckAppointmentUpdate(Map<String, Object> dataMap) {
+        Map<String, Object> controlActProcess = (Map<String, Object>) dataMap.get("controlActProcess");
+        if (controlActProcess == null) {
+            throw exception(HIS_MESSAGE_PARSE_FAIL);
+        }
+        Map<String, Object> subject = (Map<String, Object>) controlActProcess.get("subject");
+        Map<String, Object> actAppointment = subject == null ? null
+                : (Map<String, Object>) subject.get("actAppointment");
+        if (actAppointment == null) {
+            throw exception(HIS_MESSAGE_PARSE_FAIL);
+        }
+
+        // 1. 鑾峰彇鐢宠缂栧彿銆�
+        //    娉ㄦ剰锛氭帴鍙f枃妗e畾涔夐绾﹀彿鍦� actAppointment.id.extension锛堝 3360270锛夛紝
+        //    鑰屽疄闄呮姤鏂囧悓鏃舵彁渚涗簡 referencedOrder[].actOrder.id.extension锛堝 2126091700001锛�
+        //    涓� S0201ECG 鍒涘缓鐨勭敵璇峰崟鍙蜂竴鑷达級銆傛湰鍦� appointment.apply_no 瀛樼殑鏄悗鑰咃紝
+        //    鍥犳**浼樺厛鍙� referencedOrder**锛屽彇涓嶅埌鍐嶅洖閫�鍒� actAppointment.id銆�
+        String applyNo = extractApplyNoFromCheckAppointment(actAppointment);
+        if (StringUtils.isEmpty(applyNo)) {
+            log.warn("[handleCheckAppointmentUpdate][鏈壘鍒扮敵璇风紪鍙凤紝蹇界暐] dataMap={}", dataMap);
+            throw exception(HIS_APPLY_NO_MISSING);
+        }
+
+        // 1.1 銆愬繀鍋氳繃婊ゃ�慡050401/S050501 鍚屾牱鏄�**鍏ㄩ櫌閫氱敤**鏈嶅姟锛屼細鎺ㄩ�佹斁灏�/瓒呭0绛�
+        //     闈炲績鐢电瀹ょ殑棰勭害鍗曪紙瀹炴祴 2026-09-17锛歋050401 109/256銆丼050501 4/19 涓洪潪蹇冪數锛�
+        //     妫�鏌ラ」鐩"鍙宠倶姝d晶浣�/2鍙锋満鎴�(DR)"銆�"褰╄秴鍙充笅鑲㈠姩鑴�/5鍙疯瘖瀹�"锛夈��
+        //     姝ゅ墠璇ュ鐞嗗櫒**娌℃湁浠讳綍杩囨护**锛屽鑷磋繖浜涘崟琚綋浣滃績鐢电敵璇峰崟閲囬泦鍏ュ簱銆�
+        //     闈炲績鐢靛崟鐩存帴璺宠繃锛堝彧璁版棩蹇椼�佷笉钀藉簱銆佷笉杩斿洖閿欒锛岄伩鍏� HIS 鍙嶅閲嶆帹锛夈��
+        Map<String, Object> zdyNode = (Map<String, Object>) actAppointment.get("ZDY");
+        String checkDeptCode = extractDeptCodeFromCheckAppointment(actAppointment);
+        if (!hisEcgFilter.isEcgOrderWithZdy("S050401/S050501", applyNo, checkDeptCode, zdyNode)) {
+            return;
+        }
+
+        // 2. 瑙f瀽棰勭害鏃堕棿锛堝彲鑳戒负绌猴紝鍘熷疄鐜颁細鐩存帴鎶涘紓甯革級
+        Map<String, Object> zdy = zdyNode;
+        Map<String, Object> effectiveTime = (Map<String, Object>) actAppointment.get("effectiveTime");
+        Map<String, Object> anyTime = effectiveTime == null ? null
+                : (Map<String, Object>) effectiveTime.get("any");
+        String bookTimeStr = anyTime == null ? null : (String) anyTime.get("value");
+        LocalDateTime bookTime = parseDateTime(bookTimeStr);
+        LocalDate bookDate = bookTime == null ? null : bookTime.toLocalDate();
+
+        // 3. 鏃堕棿娈碉紙ZDY.time锛屽舰濡� "2026-09-17 08:30-09:00"锛夛紝瑙f瀽澶辫触涓嶅奖鍝嶅叾浠栧瓧娈�
+        Integer bookTimeslot = parseBookTimeslot(zdy == null ? null : (String) zdy.get("time"));
+
+        // 4. 灏辫瘖绫诲埆 鈫� bookSrc锛堟枃妗o細actAppointment.code.code锛屼綇闄�/闂ㄨ瘖/鎬ヨ瘖浠g爜锛�
+        Map<String, Object> code = (Map<String, Object>) actAppointment.get("code");
+        String bookSrc = code == null ? null : (String) code.get("code");
+        Integer bookSrcValue = parseIntegerQuietly(bookSrc);
+
+        // 5. 棰勭害鐘舵�佸綊涓�鍖栵紙ZDY.statusCode锛�
+        String rawStatus = zdy == null || zdy.get("statusCode") == null
+                ? null : zdy.get("statusCode").toString();
+        String status = HisCheckStatusEnum.normalize(rawStatus);
+        if (status == null && rawStatus != null) {
+            log.warn("[handleCheckAppointmentUpdate][棰勭害鐘舵�佺紪鐮佷笉鍦ㄧ紪鐮佽〃鍐咃紝灏嗕笉鏇存柊鐘舵�乚 applyNo={} rawStatus={}",
+                    applyNo, rawStatus);
+        }
+
+        // 6. 鍖归厤鏈湴璁板綍
+        AppointmentDO appointment = appointmentMapper.getByApplyNo(applyNo);
+        if (appointment == null) {
+            log.warn("[handleCheckAppointmentUpdate][鏈壘鍒伴绾﹁褰曪紝鎵ц瀹归敊鏂板] applyNo={} status={}",
+                    applyNo, status);
+            insertAppointmentFromCheckAppointment(applyNo, actAppointment, bookDate, bookTime,
+                    bookTimeslot, bookSrcValue, status);
+            return;
+        }
+
+        // 7. 鏇存柊宸叉湁璁板綍
+        if (bookDate != null) {
+            appointment.setBookDate(bookDate);
+        }
+        if (bookTime != null) {
+            appointment.setBookTime(bookTime);
+        }
+        if (bookTimeslot != null) {
+            appointment.setBookTimeslot(bookTimeslot);
+        }
+        if (bookSrcValue != null) {
+            appointment.setBookSrc(bookSrcValue);
+        }
+        if (status != null) {
+            appointment.setStatus(status);
+        }
+        // 鈥斺�� HIS 琛ュ厖瀛楁锛堥殢琛ㄦ柊澧炲垪涓�璧峰惎鐢級鈥斺��
+        // 娉ㄦ剰锛歳emark 鍙兘闀胯揪鏁扮櫨瀛楃锛堝瀹炴祴蹇冪數鍥捐繍鍔ㄨ瘯楠屾敞鎰忎簨椤� 474 瀛楋級锛�
+        // 蹇呴』鎸夊垪瀹芥埅鏂紝鍚﹀垯 Data too long 浼氬鑷存暣涓簨鍔″洖婊氾紙瑙� truncate 鏂规硶璇存槑锛�
+        if (zdy != null) {
+            Object remark = zdy.get("remark");
+            if (remark != null && StringUtils.isNotEmpty(remark.toString())) {
+                appointment.setRemark(truncate(remark.toString(), REMARK_MAX_LEN));
+            }
+            Object roomName = zdy.get("roomname");
+            if (roomName != null && StringUtils.isNotEmpty(roomName.toString())) {
+                appointment.setRoomName(truncate(roomName.toString(), ROOM_NAME_MAX_LEN));
+            }
+        }
+        // 棰勭害鎺掑簭鍙凤紙patient.id.item[4]锛�
+        Integer seqNum = extractBookSeqNum(actAppointment);
+        if (seqNum != null) {
+            appointment.setBookSeqNum(seqNum);
+        }
+        appointmentMapper.updateById(appointment);
+    }
+
+    /**
+     * 鍙栨鏌ラ绾︽姤鏂囦腑鐨勬墽琛�/鐢宠绉戝缂栫爜銆�
+     * <p>
+     * 鎶ユ枃璺緞锛歿@code actAppointment.subject.patient.providerOrganization.id.item.extension}
+     * 锛堝疄娴嬫牱渚� {@code ZY012}锛夈�備粎鐢ㄤ簬蹇冪數鍒ゅ畾鐨�"渚濇嵁 C"锛堥渶閰嶇疆鐧藉悕鍗曟墠鐢熸晥锛夈��
+     */
+    private String extractDeptCodeFromCheckAppointment(Map<String, Object> actAppointment) {
+        return digText(actAppointment, "subject", "patient", "providerOrganization", "id",
+                "item", "extension");
+    }
+
+    /**
+     * 棰勭害鎺掑簭鍙峰湪鎶ユ枃涓殑 root 鏍囪瘑銆�
+     * <p>
+     * 鎺ュ彛鏂囨。 6.4 鐨勭ず渚嬮噷鎶娿�岄绾︽帓搴忓彿銆嶆敞鍦� {@code item[4]} 浣嶇疆锛�
+     * 浣�**瀹為檯鎶ユ枃鐨� root 涓庝綅缃笉涓�鑷�**锛氱湡瀹炴姤鏂囷紙2026-09-17 鏃ュ織锛変负
+     * <pre>
+     *   item[0] root=2.16.156.10011.2.5.1.5    鍩烮D
+     *   item[1] root=2.16.156.10011.2.5.1.4    鎮h�匢D
+     *   item[2] root=2.16.156.10011.1.10       闂紙鎬ワ級璇婂彿
+     *   item[3] root=2.16.156.10011.1.12       浣忛櫌鍙�
+     *   item[4] root=2.16.156.10011.2.5.1.8    灏辫瘖娆℃暟   鈫� 涓嶆槸鎺掑簭鍙�
+     *   item[5] root=2.16.156.10011.2.5.1.9    灏辫瘖娴佹按鍙�
+     *   item[6] root=2.16.156.10011.2.5.1.11   棰勭害鎺掑簭鍙� 鈫� 瀹為檯鍦ㄦ
+     * </pre>
+     * 鍥犳**鎸� root 鍖归厤**鑰岄潪鎸変綅缃彇鍊硷紝閬垮厤浣嶇疆婕傜Щ瀵艰嚧鍙栭敊瀛楁銆�
+     */
+    private static final String ROOT_BOOK_SEQ_NUM = "2.16.156.10011.2.5.1.11";
+
+    /**
+     * 鍙栥�岄绾︽帓搴忓彿銆嶃��
+     * <p>
+     * 鎶ユ枃璺緞锛歿@code actAppointment.subject.patient.id.item[]} 涓�
+     * {@code root=2.16.156.10011.2.5.1.11} 鐨勯偅涓�椤圭殑 {@code extension}銆�
+     *
+     * @return 鎺掑簭鍙凤紱鍙栦笉鍒版椂杩斿洖 null
+     */
+    @SuppressWarnings("unchecked")
+    private Integer extractBookSeqNum(Map<String, Object> actAppointment) {
+        List<?> items = asList(actAppointment, "subject", "patient", "id", "item");
+        for (Object it : items) {
+            if (!(it instanceof Map)) {
+                continue;
+            }
+            Map<String, Object> item = (Map<String, Object>) it;
+            Object root = item.get("root");
+            if (root != null && ROOT_BOOK_SEQ_NUM.equals(root.toString().trim())) {
+                Object ext = item.get("extension");
+                return ext == null ? null : parseIntegerQuietly(ext.toString());
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 鍙栨鏌ラ绾︽姤鏂囩殑鐢宠鍗曞彿銆�
+     * <p>
+     * 浼樺厛 {@code referencedOrder[0].actOrder.id.extension}锛堜笌 S0201ECG 鍒涘缓鐨勭敵璇峰崟鍙蜂竴鑷达紝
+     * 瀹炴祴 {@code 2126091700001}锛夛紱鍙栦笉鍒版椂鍥為��鍒� {@code actAppointment.id.extension}
+     * 锛堟帴鍙f枃妗e畾涔夌殑棰勭害鍙凤紝瀹炴祴 {@code 3360270}锛夈��
+     */
+    @SuppressWarnings("unchecked")
+    private String extractApplyNoFromCheckAppointment(Map<String, Object> actAppointment) {
+        Object referencedOrders = actAppointment.get("referencedOrder");
+        if (referencedOrders instanceof List) {
+            List<?> list = (List<?>) referencedOrders;
+            if (!list.isEmpty() && list.get(0) instanceof Map) {
+                Map<String, Object> first = (Map<String, Object>) list.get(0);
+                Map<String, Object> actOrder = (Map<String, Object>) first.get("actOrder");
+                if (actOrder != null) {
+                    Map<String, Object> id = (Map<String, Object>) actOrder.get("id");
+                    if (id != null && id.get("extension") != null) {
+                        return id.get("extension").toString();
+                    }
+                }
+            }
+        }
+        Map<String, Object> id = (Map<String, Object>) actAppointment.get("id");
+        if (id != null && id.get("extension") != null) {
+            return id.get("extension").toString();
+        }
+        return null;
+    }
+
+    /** 渚濇嵁妫�鏌ラ绾︽姤鏂囨彃鍏ユ柊璁板綍锛圦4 瀹归敊鏂板锛� */
+    @SuppressWarnings("unchecked")
+    private void insertAppointmentFromCheckAppointment(String applyNo, Map<String, Object> actAppointment,
+                                                       LocalDate bookDate, LocalDateTime bookTime,
+                                                       Integer bookTimeslot, Integer bookSrcValue, String status) {
+        AppointmentDO appointment = new AppointmentDO();
+        appointment.setApplyNo(applyNo);
+        appointment.setBookDate(bookDate);
+        appointment.setBookTime(bookTime);
+        appointment.setBookTimeslot(bookTimeslot);
+        appointment.setBookSrc(bookSrcValue);
+        appointment.setStatus(status);
+
+        Map<String, Object> zdy = (Map<String, Object>) actAppointment.get("ZDY");
+        // ZDY 涓殑 remark锛堟敞鎰忎簨椤癸級涓� roomname锛堟埧闂村悕绉帮級宸查殢琛ㄦ柊澧炲垪钀藉簱锛�
+        // area锛堜綅缃級涓� roomcode锛堟埧闂寸紪鐮侊級鐩墠浠嶆棤瀵瑰簲鍒楋紝鏆備笉淇濆瓨銆�
+        if (zdy != null) {
+            Object remark = zdy.get("remark");
+            if (remark != null && StringUtils.isNotEmpty(remark.toString())) {
+                appointment.setRemark(truncate(remark.toString(), REMARK_MAX_LEN));
+            }
+            Object roomName = zdy.get("roomname");
+            if (roomName != null && StringUtils.isNotEmpty(roomName.toString())) {
+                appointment.setRoomName(truncate(roomName.toString(), ROOM_NAME_MAX_LEN));
+            }
+        }
+        // 棰勭害鎺掑簭鍙凤紙patient.id.item[4]锛�
+        Integer bookSeqNum = extractBookSeqNum(actAppointment);
+        if (bookSeqNum != null) {
+            appointment.setBookSeqNum(bookSeqNum);
+        }
+
+        // 鎮h�呬俊鎭�
+        Map<String, Object> subjectNode = (Map<String, Object>) actAppointment.get("subject");
+        if (subjectNode != null) {
+            Map<String, Object> patient = (Map<String, Object>) subjectNode.get("patient");
+            if (patient != null) {
+                List<?> patIdItems = Optional.ofNullable(patient.get("id"))
+                        .map(id -> (Map<String, Object>) id).map(id -> id.get("item"))
+                        .filter(it -> it instanceof List).map(it -> (List<?>) it)
+                        .orElse(Collections.emptyList());
+                if (patIdItems.size() > 1) {
+                    appointment.setPatId((String) ((Map<String, Object>) patIdItems.get(1)).get("extension"));
+                }
+                if (patIdItems.size() > 2) {
+                    appointment.setOutpatientNo((String) ((Map<String, Object>) patIdItems.get(2)).get("extension"));
+                }
+                if (patIdItems.size() > 3) {
+                    appointment.setHospitalNo((String) ((Map<String, Object>) patIdItems.get(3)).get("extension"));
+                }
+                // 棰勭害鎺掑簭鍙峰凡鍦ㄤ笂鏂归�氳繃 extractBookSeqNum 缁熶竴璧嬪�硷紝姝ゅ涓嶅啀閲嶅澶勭悊
+                // 鎮h�呭鍚�
+                Optional.ofNullable(patient.get("patientPerson"))
+                        .map(p -> (Map<String, Object>) p)
+                        .map(p -> (Map<String, Object>) p.get("name"))
+                        .map(n -> (Map<String, Object>) n.get("item"))
+                        .map(i -> (Map<String, Object>) i.get("part"))
+                        .map(part -> (String) part.get("value"))
+                        .ifPresent(appointment::setPatName);
+                // 鐥呬汉绉戝锛坧roviderOrganization锛夆�斺�� Q7 鍚敤
+                Optional.ofNullable(patient.get("providerOrganization"))
+                        .map(org -> (Map<String, Object>) org)
+                        .ifPresent(org -> {
+                            Optional.ofNullable(org.get("id"))
+                                    .map(id -> (Map<String, Object>) id)
+                                    .map(id -> (Map<String, Object>) id.get("item"))
+                                    .map(item -> (Map<String, Object>) item)
+                                    .map(item -> (String) item.get("extension"))
+                                    .ifPresent(appointment::setPatDeptCode);
+                            Optional.ofNullable(org.get("name"))
+                                    .map(n -> (Map<String, Object>) n)
+                                    .map(n -> (Map<String, Object>) n.get("item"))
+                                    .map(i -> (Map<String, Object>) i.get("part"))
+                                    .map(part -> (String) part.get("value"))
+                                    .ifPresent(appointment::setPatDeptDesc);
+                        });
+            }
+        }
+
+        if (StringUtils.isEmpty(appointment.getPatName())) {
+            log.warn("[insertAppointmentFromCheckAppointment][瀹归敊鏂板鐨勮褰曠己灏戞偅鑰呭鍚峕 applyNo={}", applyNo);
+        }
+        // appointment.pat_id / pat_name 鍦ㄦ暟鎹簱涓负 NOT NULL锛�
+        // 缂哄�兼椂鑻ョ洿鎺� insert 浼氭姏 SQL 寮傚父骞跺鑷存暣涓簨鍔″洖婊氥��
+        // 杩欓噷鎻愬墠缁欏嚭鏄庣‘閿欒锛屼究浜� HIS 渚у畾浣嶆姤鏂囬棶棰樸��
+        if (StringUtils.isEmpty(appointment.getPatId())) {
+            log.error("[insertAppointmentFromCheckAppointment][鎶ユ枃缂烘偅鑰呯紪鍙�(pat_id)锛屾棤娉曟柊澧瀅 applyNo={}", applyNo);
+            throw exception(PATIENT_NOT_EXISTS);
+        }
+        if (appointment.getPatGender() == null) {
+            log.warn("[insertAppointmentFromCheckAppointment][鎶ユ枃鏈彁渚涙偅鑰呮�у埆锛屾寜鏈煡(0)鍐欏叆] applyNo={}", applyNo);
+            appointment.setPatGender(UNKNOWN_GENDER);
+        }
+        AppointmentSaveReqVO saveReqVO = BeanUtils.toBean(appointment, AppointmentSaveReqVO.class);
+        createAppointment(saveReqVO);
+    }
+
+    /** 瑙f瀽 {@code yyyyMMddHHmmss} 鏃堕棿锛屽け璐ユ垨涓虹┖杩斿洖 null锛堜笉鎶涘紓甯革級 */
+    private LocalDateTime parseDateTime(String value) {
+        if (StringUtils.isEmpty(value)) {
+            return null;
+        }
+        try {
+            return LocalDateTime.parse(value.trim(), DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
+        } catch (Exception e) {
+            log.warn("[parseDateTime][鏃堕棿鏍煎紡闈炴硶锛屽拷鐣 value={}", value);
+            return null;
+        }
+    }
+
+    /** 瑙f瀽 ZDY.time锛堝舰濡� "2026-09-17 08:30-09:00"锛変负 hhmmhhmm 鏁存暟锛屽け璐ヨ繑鍥� null */
+    private Integer parseBookTimeslot(String timeRange) {
+        if (StringUtils.isEmpty(timeRange)) {
+            return null;
+        }
+        try {
+            String[] parts = timeRange.trim().split("\\s+");
+            if (parts.length < 2) {
+                return null;
+            }
+            String[] times = parts[1].split("-");
+            if (times.length < 2) {
+                return null;
+            }
+            String startTime = times[0].trim().replace(":", "");
+            String endTime = times[1].trim().replace(":", "");
+            return Integer.parseInt(startTime) * 10000 + Integer.parseInt(endTime);
+        } catch (Exception e) {
+            log.warn("[parseBookTimeslot][鏃堕棿娈佃В鏋愬け璐ワ紝蹇界暐] timeRange={}", timeRange);
+            return null;
+        }
+    }
+
+    /** 瀹芥澗鏁存暟瑙f瀽锛氶潪鏁板瓧鎴栫┖杩斿洖 null锛堥伩鍏� Integer.parseInt(null) 鎶涘紓甯革級 */
+    private Integer parseIntegerQuietly(String value) {
+        if (StringUtils.isEmpty(value) || !value.trim().matches("-?\\d+")) {
+            return null;
+        }
+        try {
+            return Integer.parseInt(value.trim());
+        } catch (NumberFormatException e) {
+            return null;
+        }
+    }
+
+    /** 瀹芥澗 Byte 瑙f瀽锛氶潪鏁板瓧鎴栫┖杩斿洖 null锛堢敤浜庢偅鑰呮�у埆绛� tinyint 瀛楁锛� */
+    private Byte parseByteQuietly(String value) {
+        if (StringUtils.isEmpty(value) || !value.trim().matches("-?\\d+")) {
+            return null;
+        }
+        try {
+            return Byte.parseByte(value.trim());
+        } catch (NumberFormatException e) {
+            return null;
+        }
+    }
+
+    @Override
+    public void handleAppointmentStatusUpdate(Map<String, Object> dataMap) {
+        if (dataMap == null) {
+            throw exception(APPOINTMENT_STATUS_REGISTER_UPDATE_FAIL);
+        }
+        //鑾峰彇applyNo
+        Map<String, Object> controlActProcess = (Map<String, Object>) dataMap.get("controlActProcess");
+        Map<String, Object> subject = (Map<String, Object>) controlActProcess.get("subject");
+        Map<String, Object> placerGroup = (Map<String, Object>) subject.get("placerGroup");
+        Map<String, Object> component2 = (Map<String, Object>) placerGroup.get("component2");
+        Map<String, Object> observationRequest = (Map<String, Object>) component2.get("observationRequest");
+        Map<String, Object> id = (Map<String, Object>) observationRequest.get("id");
+        Map<String, Object> item = (Map<String, Object>) id.get("item");
+        String applyNo = (String) item.get("extension");
+
+        //鑾峰彇status
+        Map<String, Object> component1 = (Map<String, Object>) observationRequest.get("component1");
+        Map<String, Object> processStep = (Map<String, Object>) component1.get("processStep");
+        Map<String, Object> codeMap = (Map<String, Object>) processStep.get("code");
+        String status = String.valueOf(codeMap.get("code"));
+        Date date = null;
+        if (status.equals("3")) {
+            //璁板綍鐧昏鏃堕棿
+            date = new Date();
+        }
+        // 銆愯瘖鏂棩蹇椼�戠櫥璁版椂闂村啓鍏ュ墠鍚庡甫绫诲瀷淇℃伅锛岀敤浜庡揩閫熷畾浣嶃��00:00:00 鎴柇銆嶇被闂銆�
+        //
+        // 鑳屾櫙锛�2026-09-27 璧� appointment.register_date 鍑虹幇澶ч噺 yyyy-MM-dd 00:00:00锛�
+        //       缁忓疄楠岀‘璁ゆ垚鍥犳槸銆屽啓鍏ュ�兼槸 DATE 璇箟鐨勫�笺�嶈�岄潪椹卞姩闂锛�
+        //         路 java.util.Date + DATETIME 鍒� 鈫� 淇濈暀鏃跺垎绉�
+        //         路 java.sql.Date  / DATE 鍒�     鈫� 蹇呯劧鍙樻垚 00:00:00
+        //      鏈棩蹇楁墦鍗� date 鐨勫疄闄呯被鍚嶄笌鍊硷紝鑻ユ棩蹇楅噷鍑虹幇 java.sql.Date 鎴栭潪 3 鐘舵�佸嵈琚啓锛�
+        //      鍗冲彲鐩存帴瀹氫綅鍐欏叆鏂广��
+        if (date != null) {
+            log.info("[handleAppointmentStatusUpdate][鐧昏鏃堕棿鍐欏叆] applyNo={} status={} "
+                            + "鏃ユ湡瀵硅薄绫诲瀷={} 鍊�={}锛堟椂闂撮儴鍒嗗簲涓哄綋鍓嶆椂鍒伙紝鑻ヤ负 00:00:00 璇存槑琚埅鏂級",
+                    applyNo, status, date.getClass().getName(),
+                    new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date));
+        }
+        //鐘舵�佹洿鏂�
+        Integer i = appointmentMapper.updateStatusByApplyNo(applyNo, status, date);
+        log.info("鐢宠鍗曠櫥璁癮pplyNo:{},鐘舵�乻tatus:{},鏇存柊缁撴灉锛歿}", applyNo, status, i);
+
+    }
+
+    public AppointmentDO getByApplyNo(String applyNo) {
+        return appointmentMapper.getByApplyNo(applyNo);
+    }
 }
 

--
Gitblit v1.9.3