From 7191c6d38f03f2e1baf39f959227ce9c038e4e96 Mon Sep 17 00:00:00 2001
From: liusheng <337615773@qq.com>
Date: 星期二, 03 十一月 2026 14:39:52 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master' into smartor_ls_261012

---
 ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtask.java                                                |   25 
 ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/CollectHISServiceImpl.java                               |   46 
 ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/CollectHISController.java                                  |    9 
 ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java                                           |    4 
 ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java                                            |   25 
 ruoyi-patarchive/src/main/java/com/ruoyi/patarchive/domain/PatArchiveReq.java                                     |   12 
 ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ICollectHISService.java                                       |    8 
 smartor/src/main/java/com/smartor/domain/PatChildArchive.java                                                     |   40 
 ruoyi-common/src/main/java/com/ruoyi/common/utils/HttpUtil.java                                                   |   22 
 smartor/src/main/java/com/smartor/domain/PatMaternityArchive.java                                                 |    8 
 ruoyi-patarchive/src/main/resources/mapper/patarchive/PatArchiveMapper.xml                                        |   50 
 smartor/src/main/java/com/smartor/domain/ServiceSubtaskExportVO.java                                              |    6 
 ruoyi-task/src/main/java/com/ruoyi/task/domain/entity/ServiceSubtaskEntity.java                                   |   30 
 ruoyi-system/src/main/java/com/ruoyi/system/mapper/ext/SysDept2Mapper.java                                        |    5 
 ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceTaskServiceImpl.java                                  |   89 
 ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java                                       |   12 
 ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java                                             |    9 
 smartor/src/main/java/com/smartor/domain/PatMaternityArchiveDetail.java                                           |   31 
 ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceTaskScheduleServiceImpl.java                          |   73 
 ruoyi-patarchive/src/main/java/com/ruoyi/patarchive/service/IPatSpecificArchiveGenerateConfigService.java         |   30 
 ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/PatMedInhospController.java                            |   25 
 smartor/src/main/java/com/smartor/service/IPatMaternityArchiveDetailService.java                                  |    2 
 ruoyi-system/src/main/resources/mapper/system/ext/SysDept2Mapper.xml                                              |   16 
 ruoyi-task/src/main/resources/mapper/task/ServiceTaskMapper.xml                                                   |    8 
 ruoyi-common/src/main/java/com/ruoyi/common/utils/TreeUtils.java                                                  |   32 
 ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceTaskVO.java                                                 |    7 
 ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/PatChildArchiveDetailController.java                   |   13 
 ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskCountReq.java                                        |   18 
 ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/SmsController.java                                      |   81 
 ruoyi-quartz/src/main/resources/mapper/quartz/CollectHISMapper.xml                                                |   66 
 ruoyi-patarchive/src/main/java/com/ruoyi/patarchive/mapper/PatArchiveMapper.java                                  |    8 
 ruoyi-task/src/main/java/com/ruoyi/task/service/IServiceTaskdeptService.java                                      |    8 
 ruoyi-patarchive/src/main/java/com/ruoyi/patarchive/service/impl/PatSpecificArchiveGenerateConfigServiceImpl.java |   88 
 ruoyi-admin/src/main/java/com/ruoyi/web/component/RedisMqReceiver.java                                            |  203 +
 ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceSubtaskMapper.java                                          |   39 
 smartor/src/main/java/com/smartor/service/impl/PatMaternityArchiveServiceImpl.java                                |  145 +
 smartor/src/main/java/com/smartor/service/impl/PatChildArchiveServiceImpl.java                                    |  122 +
 smartor/src/main/java/com/smartor/service/impl/PatMaternityArchiveDetailServiceImpl.java                          |    6 
 smartor/src/main/java/com/smartor/mapper/PatChildArchiveDetailMapper.java                                         |    2 
 ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceTask.java                                                   |    7 
 smartor/src/main/java/com/smartor/mapper/PatMaternityArchiveDetailMapper.java                                     |    2 
 ruoyi-task/src/main/resources/mapper/task/ServiceTaskdeptMapper.xml                                               |    3 
 ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/PatMaternityArchiveController.java                     |   34 
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java                                  |    3 
 ruoyi-task/src/main/java/com/ruoyi/task/common/ScheduleSubtaskBuilder.java                                        |   11 
 ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml                                                   |   20 
 smartor/src/main/java/com/smartor/service/impl/ServiceExternalServiceImpl.java                                    |  303 ++
 ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/PatChildArchiveController.java                         |   33 
 ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceTaskdeptServiceImpl.java                              |    5 
 smartor/src/main/java/com/smartor/service/IPatChildArchiveService.java                                            |    5 
 ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceSubtaskServiceImpl.java                               |  918 +++++++++
 smartor/src/main/resources/mapper/smartor/PatChildArchiveMapper.xml                                               |   48 
 smartor/src/main/resources/mapper/smartor/PatMaternityArchiveDetailMapper.xml                                     |   38 
 ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml                                                   |    1 
 smartor/src/main/java/com/smartor/service/impl/PatMedInhospServiceImpl.java                                       |  220 ++
 smartor/src/main/java/com/smartor/service/impl/SvyLibTemplateScriptServiceImpl.java                               |   40 
 ruoyi-task/src/main/java/com/ruoyi/task/service/IServiceSubtaskService.java                                       |   27 
 ruoyi-system/src/main/resources/mapper/system/SysUserDeptMapper.xml                                               |   11 
 ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskStatistic.java                                       |   63 
 ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceExternalController.java                         |   22 
 ruoyi-patarchive/src/main/java/com/ruoyi/patarchive/controller/PatSpecificArchiveGenerateConfigController.java    |  118 +
 ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/PatArchive.java                                    |   13 
 ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java                                          |   10 
 ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskRes.java                                             |   18 
 ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java                                  |   12 
 ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/PatMaternityArchiveDetailController.java               |   13 
 ruoyi-task/src/main/java/com/ruoyi/task/domain/VO/ServiceSubtaskVO.java                                           |   30 
 smartor/src/main/java/com/smartor/service/IPatChildArchiveDetailService.java                                      |    2 
 ruoyi-patarchive/src/main/resources/mapper/patarchive/PatSpecificArchiveGenerateConfigMapper.xml                  |  134 +
 ruoyi-patarchive/src/main/java/com/ruoyi/patarchive/mapper/PatSpecificArchiveGenerateConfigMapper.java            |   28 
 ruoyi-admin/src/main/resources/application-hzszlyy.yml                                                            |    4 
 smartor/src/main/resources/mapper/smartor/PatChildArchiveDetailMapper.xml                                         |    8 
 ruoyi-patarchive/src/main/java/com/ruoyi/patarchive/domain/PatSpecificArchiveGenerateConfig.java                  |   60 
 ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserDeptMapper.java                                         |   12 
 ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskExport.java                                          |    7 
 smartor/src/main/java/com/smartor/service/impl/PatChildArchiveDetailServiceImpl.java                              |    6 
 ruoyi-task/src/main/resources/mapper/task/ServiceSubtaskMapper.xml                                                |  332 +++
 smartor/src/main/java/com/smartor/service/IPatMedInhospService.java                                               |   27 
 smartor/src/main/java/com/smartor/domain/DTO/ManualVisitDTO.java                                                  |   29 
 ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java                                                      |  148 +
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java                                  |  243 ++
 smartor/src/main/java/com/smartor/service/IPatMaternityArchiveService.java                                        |    5 
 smartor/src/main/java/com/smartor/domain/LishuiFuyouWxChatRequest.java                                            |   59 
 ruoyi-task/src/main/java/com/ruoyi/task/controller/ServiceSubtaskController.java                                  |  135 +
 ruoyi-common/src/main/java/com/ruoyi/common/utils/sms/SmsUtils.java                                               |  911 +++++++++
 smartor/src/main/resources/mapper/smartor/PatMaternityArchiveMapper.xml                                           |    1 
 86 files changed, 5,177 insertions(+), 425 deletions(-)

diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/component/RedisMqReceiver.java b/ruoyi-admin/src/main/java/com/ruoyi/web/component/RedisMqReceiver.java
index 07214cf..e8da574 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/component/RedisMqReceiver.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/component/RedisMqReceiver.java
@@ -23,6 +23,7 @@
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.math.NumberUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.data.redis.connection.Message;
@@ -632,10 +633,15 @@
                     continue;
                 }
                 //鍦ㄥ瓙浠诲姟琛ㄩ噷璁板綍涓�涓�
-                setFailPreachForm(serviceSubtask, sendPreachform, "鍏紬鍙峰彂閫佹垚鍔�", "2");
+                Boolean stateUpdated = setFailPreachForm(serviceSubtask, sendPreachform, "鍏紬鍙峰彂閫佹垚鍔�", "2");
+                if (!Boolean.TRUE.equals(stateUpdated)) {
+                    log.error("鍏紬鍙峰彂閫佹垚鍔熶絾鏇存柊瀛愪换鍔$姸鎬佸け璐ワ紝subid锛歿}锛宲reachform锛歿}", serviceSubtask.getId(), sendPreachform);
+                    continue;
+                }
                 //浠诲姟鍙戦�佽褰�
                 ServiceSubtaskRecord serviceSubtaskRecord = new ServiceSubtaskRecord();
                 serviceSubtaskRecord.setTaskid(serviceSubtask.getTaskid().toString());
+                serviceSubtaskRecord.setSubtaskId(serviceSubtask.getId());
                 serviceSubtaskRecord.setUuid(UUID.randomUUID().toString());
                 serviceSubtaskRecord.setTasktype(serviceSubtask.getType());
                 serviceSubtaskRecord.setPreachform("5");
@@ -732,15 +738,15 @@
         List<ServiceSubtaskPreachform> serviceSubtaskPreachforms = serviceSubtaskPreachformMapper.selectServiceSubtaskPreachformList(serviceSubtaskPreachform);
 
         if (CollectionUtils.isNotEmpty(serviceSubtaskPreachforms)) {
-            Optional<String> ct = serviceSubtaskPreachforms.stream().filter(item -> currentPreachform.equals(item.getPreachform())).map(ServiceSubtaskPreachform::getCompensateTime).findFirst();
-            String compensateTime = "0";
-            if (ct.isPresent()) compensateTime = ct.get();
-            int hoursToAdd = Integer.valueOf(compensateTime) * 24;
+            Optional<String> ct = serviceSubtaskPreachforms.stream()
+                    .filter(item -> Objects.equals(currentPreachform, item.getPreachform()))
+                    .map(ServiceSubtaskPreachform::getCompensateTime)
+                    .findFirst();
+            int daysToAdd = ct.map(value -> NumberUtils.toInt(value, 0)).orElse(0);
             Calendar calendar = Calendar.getInstance();
-            calendar.setTime(visitTime);
-            calendar.add(Calendar.HOUR_OF_DAY, hoursToAdd);
-            Date newDate = calendar.getTime();
-            return newDate;
+            calendar.setTime(visitTime == null ? new Date() : visitTime);
+            calendar.add(Calendar.HOUR_OF_DAY, daysToAdd * 24);
+            return calendar.getTime();
         }
         return visitTime;
     }
@@ -835,7 +841,7 @@
      */
     public Boolean setFailPreachForm(ServiceSubtask serviceSubtask, String preachform, String remark, String failSendstate) {
         log.info("^^^^^^^^^--------setFailPreachForm鐨勫叆鍙備负serviceSubtask锛歿}   锛宲reachform鍊间负锛歿}锛�  failSendstate锛歿}", serviceSubtask, preachform, failSendstate);
-        if (preachform.equals("-1")) {
+        if ("-1".equals(preachform)) {
             //璇存槑閮戒笉闇�瑕佹墽琛屼簡
             ServiceSubtaskPreachform serviceSubtaskPreachform = new ServiceSubtaskPreachform();
             serviceSubtaskPreachform.setSubid(serviceSubtask.getId());
@@ -843,73 +849,142 @@
             serviceSubtaskPreachform.setOrgid(serviceSubtask.getOrgid());
             serviceSubtaskPreachform.setSendstate(failSendstate);
             serviceSubtaskPreachform.setCurrentSendTime(new Date());
-            if (serviceSubtask.getType().equals("3") || serviceSubtask.getType().equals("4")) {
+            if (isEducationOrNotice(serviceSubtask) && "2".equals(failSendstate)) {
                 //濡傛灉鏄鏁欐垨閫氱煡锛屽苟涓斿彂閫佹垚鍔燂紝鐩存帴灏嗗彂閫佺姸鎬佹敼鎴�9
-                if (failSendstate.equals("2")) serviceSubtaskPreachform.setSendstate("9");
+                serviceSubtaskPreachform.setSendstate("9");
             }
             serviceSubtaskPreachformMapper.updateSSPByCondition(serviceSubtaskPreachform);
 
             return true;
         }
 
-        //灏嗙姸鎬佽缃负澶辫触
-        ServiceSubtaskPreachform serviceSubtaskPreachform = new ServiceSubtaskPreachform();
-        serviceSubtaskPreachform.setSubid(serviceSubtask.getId());
-        serviceSubtaskPreachform.setTaskid(serviceSubtask.getTaskid());
-        serviceSubtaskPreachform.setOrgid(serviceSubtask.getOrgid());
-        //鑾峰彇璇ユ偅鑰呯殑鍙戦�佹柟寮忔�绘暟
-        List<ServiceSubtaskPreachform> spSize = serviceSubtaskPreachformMapper.selectServiceSubtaskPreachformList(serviceSubtaskPreachform);
-        log.error("-------spSize鐨勫�间负锛歿}", spSize);
-        serviceSubtaskPreachform.setPreachform(preachform);
-        //1:鏈彂閫佺姸鎬�
-        serviceSubtaskPreachform.setSendstate("1");
-        log.info("---serviceSubtaskPreachform鍏ュ弬涓猴細{}", serviceSubtaskPreachform);
-        List<ServiceSubtaskPreachform> serviceSubtaskPreachforms = serviceSubtaskPreachformMapper.selectServiceSubtaskPreachformList(serviceSubtaskPreachform);
-        log.info("---serviceSubtaskPreachform杩斿弬涓猴細{}", serviceSubtaskPreachforms);
-        //鐢佃瘽鐨勬湭鍙戦�佺姸鎬侊紝闇�瑕佹煡2锛屽洜涓猴紝鏁版嵁鍙兘鍗″湪闃熷垪閲屼簡(涔熸湁鍙兘娌℃湁鎷夊彇锛宻endstate=1,鎵�浠ュ鏋滄槸鐢佃瘽鐘舵�佸垽鏂璼erviceSubtaskPreachforms鏄笉鏄负绌�)
-        if (preachform.equals("3") && CollectionUtils.isEmpty(serviceSubtaskPreachforms)) {
-            serviceSubtaskPreachform.setSendstate("2");
-            serviceSubtaskPreachforms = serviceSubtaskPreachformMapper.selectServiceSubtaskPreachformList(serviceSubtaskPreachform);
-        }
-        if (CollectionUtils.isNotEmpty(serviceSubtaskPreachforms)) {
-            ServiceSubtaskPreachform serviceSubtaskPreachform1 = serviceSubtaskPreachforms.get(0);
-            serviceSubtaskPreachform1.setSendstate(failSendstate);
-            if (serviceSubtask.getType().equals("3") || serviceSubtask.getType().equals("4")) {
-                //濡傛灉鏄鏁欐垨閫氱煡锛屽苟涓斿彂閫佹垚鍔燂紝鐩存帴灏嗗彂閫佺姸鎬佹敼鎴�9
-                if (failSendstate.equals("2")) serviceSubtaskPreachform1.setSendstate("9");
-            }
-            serviceSubtaskPreachform1.setRemark(remark);
-            serviceSubtaskPreachformMapper.updateServiceSubtaskPreachform(serviceSubtaskPreachform1);
-            //濡傛灉褰撳墠鐨刾reachform宸茬粡鏄渶鍚庝竴涓簡,骞朵笖鏈杩樻槸鍙戦�佸け璐ワ紝閭g洿鎺ュ皢serviceSubtask鐨剆endstate鐘舵�佹敼鎴�5灏辫浜�(鍏ㄥけ璐ヤ簡)
-            if (CollectionUtils.isNotEmpty(spSize) && serviceSubtaskPreachform1.getSort() == spSize.size()
-                    && isSubtaskDeadline(serviceSubtask, serviceSubtaskPreachforms)) {
-                if (serviceSubtaskPreachform1.getSort() == spSize.size()) {
-                    if (failSendstate.equals("4") || failSendstate.equals("5")) {
-                        serviceSubtask.setCurrentPreachform(preachform);
-                        serviceSubtask.setSendstate(7L);
-                        serviceSubtask.setFinishtime(null);
-                        serviceSubtask.setRemark("澶勭悊琛ュ伩浠诲姟,褰撳墠澶勭悊鏈�鍚庤ˉ鍋匡紝鍏ㄩ儴鎵ц澶辫触(瓒呮椂)");
-                        serviceSubtaskMapper.updateServiceSubtask(serviceSubtask);
-                        return true;
-                    }
-                }
-            }
-        } else {
+        // 鏌ヨ璇ュ瓙浠诲姟鐨勫叏閮ㄥ彂閫佹柟寮忥紝涓嶅啀鍙煡 sendstate=1銆�
+        // 绗竴娆″彂閫佸悗璇ヨ鍙兘宸茬粡鍙樻垚 sendstate=2锛岄噸璇曟椂浠嶉渶瑕佸畾浣嶅埌鍚屼竴琛屻��
+        ServiceSubtaskPreachform query = new ServiceSubtaskPreachform();
+        query.setSubid(serviceSubtask.getId());
+        query.setTaskid(serviceSubtask.getTaskid());
+        query.setOrgid(serviceSubtask.getOrgid());
+        List<ServiceSubtaskPreachform> allPreachforms = serviceSubtaskPreachformMapper.selectServiceSubtaskPreachformList(query);
+        log.info("---serviceSubtaskPreachform杩斿弬涓猴細{}", allPreachforms);
+        if (CollectionUtils.isEmpty(allPreachforms)) {
             log.error("銆怣Q涓鎮h�呮病鏈夋煡璇㈠埌灞炰簬浠栫殑鍙戦�佹柟寮忥紝subid锛歿},prechform:{},orgid:{}銆�", serviceSubtask.getId(), preachform, serviceSubtask.getOrgid());
             return false;
         }
-        log.info("^^^^^^^^^--------preachform鐨勫�间负锛歿}", preachform);
+
+        // 鎸� getSendPreachform 鐨勯�夋嫨瑙勫垯瀹氫綅鏈瀹為檯澶勭悊鐨勫彂閫佹柟寮忚銆�
+        ServiceSubtaskPreachform currentPreachformRow = findCurrentPreachformRow(allPreachforms, serviceSubtask, preachform);
+        if (currentPreachformRow == null) {
+            log.error("銆怣Q涓鎮h�呮病鏈夋煡璇㈠埌褰撳墠鍙戦�佹柟寮忥紝subid锛歿},prechform:{},orgid:{}銆�", serviceSubtask.getId(), preachform, serviceSubtask.getOrgid());
+            return false;
+        }
+
+        // 鍏堣惤搴� service_subtask 鐘舵�侊紝鍐嶆洿鏂板彂閫佹柟寮忔槑缁嗐��
+        // 杩欐牱鍗充娇鍚庣画姝ラ寮傚父锛�60 绉掗噸璇曚篃涓嶄細鍐嶆閫変腑骞跺彂閫併��
         serviceSubtask.setCurrentPreachform(preachform);
-        serviceSubtask.setVisitTime(getNextVisitTime(serviceSubtask.getId(), serviceSubtask.getTaskid(), serviceSubtask.getVisitTime(), serviceSubtask.getCurrentPreachform()));
+        serviceSubtask.setVisitTime(getNextVisitTime(serviceSubtask.getId(), serviceSubtask.getTaskid(), serviceSubtask.getVisitTime(), preachform));
         serviceSubtask.setSendstate(3L);
-        if (serviceSubtask.getType().equals("3") || serviceSubtask.getType().equals("4")) {
+        if (isEducationOrNotice(serviceSubtask) && "2".equals(failSendstate)) {
             //濡傛灉鏄鏁欐垨閫氱煡锛屽苟涓斿彂閫佹垚鍔燂紝鍒欑洿鎺ュ皢serviceSubtask鐘舵�佹敼鎴�6L锛堣繖涓偅鑰呮槸娌℃湁棰樺仛鐨勶紝涓嶄細鍐嶈Е鍙戞帴鍙h繘琛岀姸鎬佷慨鏀癸級
-            if (failSendstate.equals("2")) {
-                serviceSubtask.setSendstate(6L);
-            }
+            serviceSubtask.setSendstate(6L);
         }
         serviceSubtaskMapper.updateServiceSubtask(serviceSubtask);
+
+        // 鏇存柊褰撳墠鍙戦�佹柟寮忔槑缁嗭紱鍙戦�佹垚鍔熸椂鍏佽褰撳墠琛屽凡缁忔槸 sendstate=2銆�
+        currentPreachformRow.setSendstate(failSendstate);
+        if (isEducationOrNotice(serviceSubtask) && "2".equals(failSendstate)) {
+            currentPreachformRow.setSendstate("9");
+        }
+        currentPreachformRow.setRemark(remark);
+        serviceSubtaskPreachformMapper.updateServiceSubtaskPreachform(currentPreachformRow);
+
+        // 鍙湁鏈�鍚庝竴姝ヤ笖鍙戦�佸け璐ワ紙4/5锛夋椂鎵嶉渶瑕佸垽鏂秴鏃跺苟缃负 7銆�
+        boolean lastStep = currentPreachformRow.getSort() != null
+                && currentPreachformRow.getSort() == allPreachforms.size();
+        if (lastStep && ("4".equals(failSendstate) || "5".equals(failSendstate))
+                && isSubtaskDeadline(serviceSubtask, allPreachforms)) {
+            serviceSubtask.setSendstate(7L);
+            serviceSubtask.setFinishtime(null);
+            serviceSubtask.setRemark("澶勭悊琛ュ伩浠诲姟,褰撳墠澶勭悊鏈�鍚庤ˉ鍋匡紝鍏ㄩ儴鎵ц澶辫触(瓒呮椂)");
+            serviceSubtaskMapper.updateServiceSubtask(serviceSubtask);
+        }
         return true;
+    }
+
+    /**
+     * 鍒ゆ柇鏄惁涓哄鏁欐垨閫氱煡绫诲瀷銆�
+     */
+    private boolean isEducationOrNotice(ServiceSubtask serviceSubtask) {
+        return serviceSubtask != null
+                && ("3".equals(serviceSubtask.getType()) || "4".equals(serviceSubtask.getType()));
+    }
+
+    /**
+     * 瀹氫綅鏈瀹為檯澶勭悊鐨勫彂閫佹柟寮忚銆�
+     * 閫夋嫨瑙勫垯涓� getSendPreachform 淇濇寔涓�鑷达細
+     * 1. current_preachform 涓虹┖鏃跺彇 sort=1锛�
+     * 2. current_preachform 涓嶄负绌烘椂鍙� current_preachform 瀵瑰簲 sort 鐨勪笅涓�姝ワ紱
+     * 3. 鍏滃簳鎸夊彂閫佹柟寮忓尮閰嶏紝浼樺厛鍙� current_send_time 鏈�杩戠殑涓�琛岋紝鍐嶅彇 sort 杈冨皬鐨勪竴琛屻��
+     */
+    private ServiceSubtaskPreachform findCurrentPreachformRow(List<ServiceSubtaskPreachform> rows,
+                                                              ServiceSubtask serviceSubtask,
+                                                              String preachform) {
+        if (StringUtils.isEmpty(serviceSubtask.getCurrentPreachform())) {
+            for (ServiceSubtaskPreachform row : rows) {
+                if (Objects.equals(preachform, row.getPreachform())
+                        && row.getSort() != null && row.getSort() == 1L) {
+                    return row;
+                }
+            }
+        } else {
+            Long currentSort = null;
+            for (ServiceSubtaskPreachform row : rows) {
+                if (Objects.equals(serviceSubtask.getCurrentPreachform(), row.getPreachform())) {
+                    currentSort = row.getSort();
+                    break;
+                }
+            }
+            if (currentSort != null) {
+                Long nextSort = currentSort + 1;
+                for (ServiceSubtaskPreachform row : rows) {
+                    if (Objects.equals(nextSort, row.getSort())
+                            && Objects.equals(preachform, row.getPreachform())) {
+                        return row;
+                    }
+                }
+            }
+        }
+
+        ServiceSubtaskPreachform target = null;
+        for (ServiceSubtaskPreachform row : rows) {
+            if (!Objects.equals(preachform, row.getPreachform())) {
+                continue;
+            }
+            if (target == null) {
+                target = row;
+                continue;
+            }
+            Date rowTime = row.getCurrentSendTime();
+            Date targetTime = target.getCurrentSendTime();
+            if (rowTime != null && (targetTime == null || rowTime.after(targetTime))) {
+                target = row;
+            } else if (Objects.equals(rowTime, targetTime) && comparePreachformSort(row, target) < 0) {
+                target = row;
+            }
+        }
+        return target;
+    }
+
+    private int comparePreachformSort(ServiceSubtaskPreachform row, ServiceSubtaskPreachform target) {
+        if (row.getSort() == null && target.getSort() == null) {
+            return 0;
+        }
+        if (row.getSort() == null) {
+            return 1;
+        }
+        if (target.getSort() == null) {
+            return -1;
+        }
+        return row.getSort().compareTo(target.getSort());
     }
 
     private String getKey() {
@@ -959,11 +1034,15 @@
             return false;
         }
         // 鍩哄噯鏃跺埢锛氬嚭闄㈡椂闂� + 鍙戦�佹棩锛坰endDay 缂虹渷鎸� 1锛夛紝骞跺厹搴曢伩鍏� NPE/鑴忔暟鎹�
+        if (serviceSubtask.getEndtime() == null) {
+            log.warn("銆恑sSubtaskDeadline銆憇ubid锛歿} endtime涓虹┖锛屾棤娉曡绠楄秴鏃讹紝鎸夋湭瓒呮椂澶勭悊", serviceSubtask.getId());
+            return false;
+        }
         ServiceTask serviceTask = serviceTaskMapper.selectServiceTaskByTaskid(serviceSubtask.getTaskid());
         int sendDay = serviceTask != null && serviceTask.getSendDay() != null ? serviceTask.getSendDay().intValue() : 1;
         Date deadline = DateUtils.addDays(serviceSubtask.getEndtime(), sendDay);
         int sumCompensateTime = CollectionUtils.isEmpty(serviceSubtaskPreachforms) ? 0 : serviceSubtaskPreachforms.stream()
-                .mapToInt(x -> x.getCompensateTime() == null ? 0 : Integer.parseInt(x.getCompensateTime()))
+                .mapToInt(x -> NumberUtils.toInt(x.getCompensateTime(), 0))
                 .sum();
         sumCompensateTime = sumCompensateTime + 1;
         // 鍙ˉ鍋挎渶鍚庝竴姝ヨ嚜韬殑琛ュ伩鏃堕棿锛堝ぉ锛夛紝鑴忓�间竴寰嬫寜 0 鍏滃簳
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/SmsController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/SmsController.java
index b55411c..c65e184 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/SmsController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/SmsController.java
@@ -1,8 +1,15 @@
 package com.ruoyi.web.controller.common;
 
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.ruoyi.common.annotation.AddOrgId;
+import com.ruoyi.common.annotation.Anonymous;
+import com.ruoyi.common.annotation.Log;
 import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.enums.BusinessType;
 import com.ruoyi.common.utils.RSAPublicKeyExample;
 import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.sms.SmsUtils;
 import com.ruoyi.message.SendResult;
 import com.ruoyi.message.sms.SmsSendRequest;
 import com.ruoyi.message.sms.SmsSenderFactory;
@@ -11,6 +18,7 @@
 import com.ruoyi.message.sms.provider.shiyi.ShiyiSmsUtil;
 import com.ruoyi.message.wechat.WechatSendRequest;
 import com.ruoyi.message.wechat.WechatSenderFactory;
+import com.ruoyi.system.service.ISysConfigService;
 import com.smartor.domain.*;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
@@ -64,6 +72,8 @@
     private IServiceOutPathService iServiceOutPathService;
     @Autowired
     private IServiceSubtaskService iServiceSubtaskService;
+    @Autowired
+    private ISysConfigService configService;
 
     @Value("${spring.profiles.active}")
     private String active;
@@ -223,4 +233,75 @@
         return AjaxResult.success(content);
     }
 
+    /**
+     * @param
+     * @return
+     */
+    @ApiOperation("鐭俊鍙戦��")
+    @AddOrgId
+    @PostMapping("/sendYunMasSms")
+    public AjaxResult sendYunMasSms(@RequestBody smsVO vo) throws Exception {
+        String yunMasConfigJson = configService.selectConfigByKey("ls.msg.yunmas", vo.getOrgid());
+        SmsUtils.YunMasConfig yunMasConfig = SmsUtils.YunMasConfig.of(yunMasConfigJson);
+        SmsUtils.SmsResult smsResult = SmsUtils.sendYunMasSms(yunMasConfig, vo.getPhone(), vo.getContent());
+        return AjaxResult.success(smsResult);
+    }
+
+    /**
+     * 涓芥按濡囧辜 寰俊娑堟伅鍙戦��
+     * <p>
+     * 澶栭儴绯荤粺浼犲叆鎮h�呬俊鎭拰閫氱煡鍐呭锛岀敱鏈郴缁熺粺涓�鑾峰彇 token銆佽В鏋愭ā鏉垮苟璋冪敤涓芥按寰俊 sendMessage 鎺ュ彛銆�
+     *
+     * @param request 寰俊娑堟伅璇锋眰
+     * @return 鍙戦�佺粨鏋�
+     */
+    @Anonymous
+    @ApiOperation(value = "涓芥按濡囧辜 寰俊娑堟伅鍙戦��", notes = "渚涗附姘村競濡囧辜淇濆仴闄㈠閮ㄨ皟鐢�")
+    @Log(title = "涓芥按濡囧辜寰俊娑堟伅鍙戦��", businessType = BusinessType.OTHER)
+    @PostMapping("/sendChatFuyou")
+    public AjaxResult sendChatFuyou(@RequestBody LishuiFuyouWxChatRequest request) {
+        if (request == null) {
+            return AjaxResult.error("璇锋眰鍙傛暟涓嶈兘涓虹┖");
+        }
+        try {
+            SmsUtils.LsWxConfig config = SmsUtils.LsWxConfig.defaultLishui();
+
+            SmsUtils.LsWxMessage message = new SmsUtils.LsWxMessage();
+            message.setId(request.getTemplateId());
+            message.setPatientId(request.getPatientId());
+            message.setPatientName(request.getPatientName());
+            message.setIdCard(request.getIdCard());
+            message.setMobile(request.getMobile());
+            message.setMedCardNo(request.getMedCardNo());
+            message.setTitleName(request.getTitleName());
+            message.setContent(request.getContent());
+            message.setUrl(request.getUrl());
+
+            log.info("銆愪附姘村骞煎井淇°�戞敹鍒板閮ㄨ皟鐢紝鎵嬫満鍙凤細{}锛屾ā鏉縄D锛歿}",
+                    request.getMobile(), request.getTemplateId());
+            String resp = SmsUtils.sendChatLs(config, message);
+            log.info("銆愪附姘村骞煎井淇°�戝閮ㄨ皟鐢ㄧ粨鏋滐細{}", resp);
+
+            JSONObject result;
+            try {
+                result = JSON.parseObject(resp);
+            } catch (Exception e) {
+                log.error("銆愪附姘村骞煎井淇°�戠涓夋柟鍝嶅簲涓嶆槸鍚堟硶JSON锛歿}", resp, e);
+                return AjaxResult.error("涓芥按濡囧辜寰俊鍙戦�佸け璐ワ細绗笁鏂瑰搷搴斿紓甯�", resp);
+            }
+            if (result == null) {
+                return AjaxResult.error("涓芥按濡囧辜寰俊鍙戦�佸け璐ワ細绗笁鏂瑰搷搴斾负绌�", resp);
+            }
+            if (result.getIntValue("code") != 0) {
+                String msg = result.getString("msg");
+                return AjaxResult.error("涓芥按濡囧辜寰俊鍙戦�佸け璐ワ細"
+                        + (StringUtils.isEmpty(msg) ? "绗笁鏂规帴鍙h繑鍥炲け璐�" : msg), result);
+            }
+            return AjaxResult.success("鍙戦�佹垚鍔�", result);
+        } catch (Exception e) {
+            log.error("銆愪附姘村骞煎井淇°�戝閮ㄨ皟鐢ㄥ紓甯革紝鎵嬫満鍙凤細{}", request.getMobile(), e);
+            return AjaxResult.error("涓芥按濡囧辜寰俊鍙戦�佸紓甯革細" + e.getMessage());
+        }
+    }
+
 }
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/PatChildArchiveController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/PatChildArchiveController.java
index 74927de..02023d8 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/PatChildArchiveController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/PatChildArchiveController.java
@@ -5,14 +5,18 @@
 import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.core.domain.model.LoginUser;
 import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.enums.BusinessType;
 import com.ruoyi.common.utils.PageUtils;
+import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.smartor.domain.PatChildArchive;
+import com.smartor.domain.PatChildArchiveDetail;
 import com.smartor.service.IPatChildArchiveService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
@@ -25,6 +29,7 @@
  * @author ruoyi
  * @date 2026-10-09
  */
+@Slf4j
 @Api(description = "鍎跨妗f")
 @RestController
 @RequestMapping("/smartor/childArchive")
@@ -85,4 +90,32 @@
     public AjaxResult remove(@PathVariable Long[] ids) {
         return toAjax(patChildArchiveService.deletePatChildArchiveByIds(ids));
     }
+
+    @ApiOperation("鎵归噺鍒犻櫎鍎跨妗f")
+    //@PreAuthorize("@ss.hasPermi('system:childArchive:remove')")
+    @Log(title = "鍎跨妗f", businessType = BusinessType.DELETE)
+    @PostMapping("/batchDelete")
+    public AjaxResult batchDelete(@RequestBody Long[] ids) {
+        return toAjax(patChildArchiveService.batchDeletePatChildArchiveByIds(ids));
+    }
+
+    /**
+     * 鏂板鎴栦慨鏀瑰効绉戞。妗�
+     */
+    @ApiOperation("鏂板鎴栦慨鏀瑰効绉戞。妗�")
+    @PostMapping("/insertOrUpdateChildArchive")
+    public AjaxResult insertOrUpdateChildArchive(@RequestBody PatChildArchiveDetail patChildArchiveDetail) {
+        log.info("insertOrUpdateChildArchive鐨勫叆鍙備负锛歿}", patChildArchiveDetail);
+        LoginUser loginUser = getLoginUser();
+        SysUser user = loginUser.getUser();
+        patChildArchiveDetail.setCreateBy(user.getNickName());
+        patChildArchiveDetail.setUpdateBy(user.getNickName());
+        patChildArchiveDetail.setOrgid(user.getOrgid());
+        if(StringUtils.isNotEmpty(user.getCampusid())){
+            patChildArchiveDetail.setCampusid(user.getCampusid());
+        }else {
+            patChildArchiveDetail.setCampusid("1");
+        }
+        return success(patChildArchiveService.insertOrUpdateChildArchive(patChildArchiveDetail));
+    }
 }
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/PatChildArchiveDetailController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/PatChildArchiveDetailController.java
index cb95eef..939c2b7 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/PatChildArchiveDetailController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/PatChildArchiveDetailController.java
@@ -13,6 +13,7 @@
 import com.smartor.service.IPatChildArchiveDetailService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import org.apache.commons.collections4.CollectionUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
@@ -53,10 +54,14 @@
     }
 
     @ApiOperation("鑾峰彇鍎跨妗f璇︽儏璇︾粏淇℃伅")
-    //@PreAuthorize("@ss.hasPermi('system:childArchiveDetail:query')")
-    @GetMapping(value = "/getInfo/{id}")
-    public AjaxResult getInfo(@PathVariable("id") Long id) {
-        return success(patChildArchiveDetailService.selectPatChildArchiveDetailById(id));
+    @GetMapping(value = "/getInfo/{archiveid}")
+    public AjaxResult getInfo(@PathVariable("archiveid") Long archiveid) {
+        PatChildArchiveDetail patChildArchiveDetail = new PatChildArchiveDetail();
+        patChildArchiveDetail.setArchiveid(archiveid);
+        List<PatChildArchiveDetail> list = patChildArchiveDetailService.selectPatChildArchiveDetailList(patChildArchiveDetail);
+        if(CollectionUtils.isNotEmpty(list)){
+            return success(list.get(0));
+        }else return success(null);
     }
 
     @ApiOperation("鏂板鍎跨妗f璇︽儏")
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/PatMaternityArchiveController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/PatMaternityArchiveController.java
index 8250a36..4ae1aae 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/PatMaternityArchiveController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/PatMaternityArchiveController.java
@@ -5,14 +5,19 @@
 import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.core.domain.model.LoginUser;
 import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.enums.BusinessType;
 import com.ruoyi.common.utils.PageUtils;
+import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.smartor.domain.PatMaternityArchive;
+import com.smartor.domain.PatMaternityArchiveDetail;
+import com.smartor.domain.SvyTaskVO;
 import com.smartor.service.IPatMaternityArchiveService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
@@ -25,6 +30,7 @@
  * @author ruoyi
  * @date 2026-10-09
  */
+@Slf4j
 @Api(description = "濡囩妗f")
 @RestController
 @RequestMapping("/smartor/maternityArchive")
@@ -85,4 +91,32 @@
     public AjaxResult remove(@PathVariable Long[] ids) {
         return toAjax(patMaternityArchiveService.deletePatMaternityArchiveByIds(ids));
     }
+
+    @ApiOperation("鎵归噺鍒犻櫎濡囩妗f")
+    //@PreAuthorize("@ss.hasPermi('system:maternityArchive:remove')")
+    @Log(title = "濡囩妗f", businessType = BusinessType.DELETE)
+    @PostMapping("/batchDelete")
+    public AjaxResult batchDelete(@RequestBody Long[] ids) {
+        return toAjax(patMaternityArchiveService.batchDeletePatMaternityArchiveByIds(ids));
+    }
+
+    /**
+     * 鏂板鎴栦慨鏀瑰绉戞。妗�
+     */
+    @ApiOperation("鏂板鎴栦慨鏀瑰绉戞。妗�")
+    @PostMapping("/insertOrUpdateMaternityArchive")
+    public AjaxResult insertOrUpdateMaternityArchive(@RequestBody PatMaternityArchiveDetail patMaternityArchiveDetail) {
+        log.info("insertOrUpdateMaternityArchive鐨勫叆鍙備负锛歿}", patMaternityArchiveDetail);
+        LoginUser loginUser = getLoginUser();
+        SysUser user = loginUser.getUser();
+        patMaternityArchiveDetail.setCreateBy(user.getNickName());
+        patMaternityArchiveDetail.setUpdateBy(user.getNickName());
+        patMaternityArchiveDetail.setOrgid(user.getOrgid());
+        if(StringUtils.isNotEmpty(user.getCampusid())){
+            patMaternityArchiveDetail.setCampusid(user.getCampusid());
+        }else {
+            patMaternityArchiveDetail.setCampusid("1");
+        }
+        return success(patMaternityArchiveService.insertOrUpdateMaternityArchive(patMaternityArchiveDetail));
+    }
 }
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/PatMaternityArchiveDetailController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/PatMaternityArchiveDetailController.java
index 0a514a9..d915f56 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/PatMaternityArchiveDetailController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/PatMaternityArchiveDetailController.java
@@ -13,6 +13,7 @@
 import com.smartor.service.IPatMaternityArchiveDetailService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import org.apache.commons.collections4.CollectionUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
@@ -53,10 +54,14 @@
     }
 
     @ApiOperation("鑾峰彇濡囩妗f璇︽儏璇︾粏淇℃伅")
-    //@PreAuthorize("@ss.hasPermi('system:maternityArchiveDetail:query')")
-    @GetMapping(value = "/getInfo/{id}")
-    public AjaxResult getInfo(@PathVariable("id") Long id) {
-        return success(patMaternityArchiveDetailService.selectPatMaternityArchiveDetailById(id));
+    @GetMapping(value = "/getInfo/{archiveid}")
+    public AjaxResult getInfo(@PathVariable("archiveid") Long archiveid) {
+        PatMaternityArchiveDetail patMaternityArchiveDetail = new PatMaternityArchiveDetail();
+        patMaternityArchiveDetail.setArchiveid(archiveid);
+        List<PatMaternityArchiveDetail> list = patMaternityArchiveDetailService.selectPatMaternityArchiveDetailList(patMaternityArchiveDetail);
+        if(CollectionUtils.isNotEmpty(list)){
+            return success(list.get(0));
+        }else return success(null);
     }
 
     @ApiOperation("鏂板濡囩妗f璇︽儏")
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/PatMedInhospController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/PatMedInhospController.java
index 4c0445d..558544c 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/PatMedInhospController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/PatMedInhospController.java
@@ -7,6 +7,7 @@
 import com.ruoyi.system.service.ISysConfigService;
 import com.ruoyi.inhospvisit.domain.PatMedInhosp;
 import com.ruoyi.common.core.service.dto.PatMedReq;
+import com.smartor.domain.DTO.ManualVisitDTO;
 import com.smartor.service.IPatMedInhospService;
 import com.smartor.service.IPatMedOuthospService;
 import io.swagger.annotations.Api;
@@ -14,6 +15,8 @@
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.web.bind.annotation.*;
+
+import java.util.Map;
 
 /**
  * 鎮h�呬綇闄㈣褰旵ontroller
@@ -79,4 +82,26 @@
     public TableDataInfo getDocAndPat(@RequestBody PatMedInhosp patMedInhosp) {
         return getDataTable(patMedInhospService.getDocAndPat(patMedInhosp));
     }
+    /**
+     * 鎮h�呬汉宸ラ殢璁匡細鍒犻櫎鏈畬鎴愰殢璁夸换鍔″苟鎸夋寚瀹氫换鍔¢噸鏂扮敓鎴�
+     */
+    @ApiOperation("addManualVisit")
+    @PostMapping("/addManualVisit")
+    public AjaxResult addManualVisit(@RequestBody ManualVisitDTO manualVisitDTO) {
+        Map<String, Object> result = patMedInhospService.addManualVisit(manualVisitDTO);
+        if (Boolean.TRUE.equals(result.get("hasDuplicate"))) {
+            return AjaxResult.warn((String) result.get("tipMessage"), result);
+        }
+        return success(result);
+    }
+
+    /**
+     * 涓存椂鏂规硶锛屽鐞哾iagcheckFlag = 0鐨勬暟鎹紝閲嶆柊鐢熸垚闅忚浠诲姟
+     */
+    @ApiOperation("dealIcd10Subtask")
+    @PostMapping("/dealIcd10Subtask")
+    public void addIcd10SubTask(@RequestBody PatMedInhosp patMedInhospVo) {
+        String config = configService.selectConfigByKey("visit.early.day");
+        iPatMedInhospService.dealIcd10Subtask(patMedInhospVo, config);
+    }
 }
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceExternalController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceExternalController.java
index 2045905..4406762 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceExternalController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceExternalController.java
@@ -6,6 +6,7 @@
 import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.core.domain.model.LoginUser;
 import com.ruoyi.common.utils.DtoConversionUtils;
+import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.system.service.ISysConfigService;
 import com.smartor.domain.*;
 import com.smartor.service.IServiceExternalService;
@@ -49,8 +50,7 @@
         try {
             aBoolean = serviceExternalService.generalInterface(dataMap);
         } catch (Exception e) {
-            e.printStackTrace();
-            log.error("serviceExternal---generalInterface鍑哄紓甯镐簡锛歿}", e.getMessage());
+            log.error("serviceExternal---generalInterface鍑哄紓甯镐簡锛歿}", e.getMessage(), e);
             aBoolean = false;
         }
         return returnParam(aBoolean, dataMap);
@@ -364,8 +364,7 @@
         try {
             aBoolean = serviceExternalService.addInspectBGService(dataMap);
         } catch (Exception e) {
-            e.printStackTrace();
-            log.error("serviceExternal---addInspectBGService锛歿}", e.getMessage());
+            log.error("serviceExternal---addInspectBGService锛歿}", e.getMessage(), e);
             aBoolean = false;
         }
         return returnParam(aBoolean, dataMap);
@@ -389,8 +388,19 @@
 
 
     private Map<String, Object> returnParam(Boolean b, Map dataMap) {
-        Map yeWuXX = (Map) dataMap.get("YeWuXX");
-        String XiaoXiID = (String) yeWuXX.get("XiaoXiID");
+        String XiaoXiID = null;
+        if (dataMap != null) {
+            Map yeWuXX = (Map) dataMap.get("YeWuXX");
+            if (yeWuXX != null && yeWuXX.get("XiaoXiID") != null) {
+                XiaoXiID = yeWuXX.get("XiaoXiID").toString();
+            }
+            if (StringUtils.isBlank(XiaoXiID)) {
+                Map xiaoXiTou = (Map) dataMap.get("XiaoXiTou");
+                if (xiaoXiTou != null && xiaoXiTou.get("XiaoXiID") != null) {
+                    XiaoXiID = xiaoXiTou.get("XiaoXiID").toString();
+                }
+            }
+        }
 
         Map<String, Object> map = new HashMap<>();
         if (b) {
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java
index d6aa16b..c681538 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java
@@ -283,6 +283,18 @@
     }
 
     /**
+     * 鏍规嵁 sys_user.hosp_info锛堢瀹わ級鍜� sys_user.dept_info锛堢梾鍖猴級琛ュ叏 sys_user_dept 鏁版嵁
+     */
+    @Log(title = "鐢ㄦ埛绠$悊", businessType = BusinessType.UPDATE)
+    @AddOrgId
+    @PostMapping("/repairUserDept")
+    public AjaxResult repairUserDept(@RequestBody(required = false) SysUser user) {
+        String orgid = user == null ? null : user.getOrgid();
+        Long userId = user == null ? null : user.getUserId();
+        return success(userService.repairUserDeptByHospAndDeptInfo(orgid, userId));
+    }
+
+    /**
      * 鑾峰彇閮ㄩ棬鏍戝垪琛�
      */
     //@PreAuthorize("@ss.hasPermi('system:user:list')")
diff --git a/ruoyi-admin/src/main/resources/application-hzszlyy.yml b/ruoyi-admin/src/main/resources/application-hzszlyy.yml
index b81265f..1a5e2af 100644
--- a/ruoyi-admin/src/main/resources/application-hzszlyy.yml
+++ b/ruoyi-admin/src/main/resources/application-hzszlyy.yml
@@ -194,8 +194,8 @@
 FTP_USERNAME: voice
 FTP_PASSWORD: xh@2023
 
-#璇煶鍦板潃璁块棶鍓嶇紑(鏂板崕)
-voicePathPrefix: http://192.168.191.181:8095/profile/upload/vadio/
+#璇煶鍦板潃璁块棶鍓嶇紑(甯備竴)
+voicePathPrefix: http://192.168.129.198/profile/upload/vadio/
 #璇煶鍦板潃璁块棶鍓嶇紑(鍏徃)
 #voicePathPrefix: http://192.168.2.13:8095/profile/upload/vadio/
 
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java
index 0b6350d..e8f5ed2 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java
@@ -33,7 +33,7 @@
     /**
      * 鍒涘缓鏃堕棿
      */
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     private Date createTime;
 
     /**
@@ -44,7 +44,7 @@
     /**
      * 鏇存柊鏃堕棿
      */
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     private Date updateTime;
 
     /**
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/PatArchive.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/PatArchive.java
index 5914177..3e281b4 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/PatArchive.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/PatArchive.java
@@ -439,4 +439,17 @@
     @ApiModelProperty(value = "鏈�鍚庣粨鏉熸椂闂�")
     private String lastEndTime;
 
+    /**
+     * 鍒涘缓鏃堕棿
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private Date createTimeStart;
+
+    /**
+     * 鍒涘缓鏃堕棿
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private Date createTimeEnd;
+
+
 }
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java
index e06e99f..905ee0d 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java
@@ -258,6 +258,18 @@
     @ApiModelProperty("婊℃剰搴﹀垎绫伙紙闂嵎/璇煶锛�")
     private Map<String, Object> satisfactionCategories = new HashMap<>();
 
+    /**
+     * HIS澧為噺鍚屾寮�濮嬫椂闂达紙瀵瑰簲 healthy_user.last_Update_Time锛�
+     */
+    @ApiModelProperty("鍚屾寮�濮嬫椂闂达紝鏍煎紡锛歽yyy-MM-dd HH:mm:ss")
+    private String lastStartTime;
+
+    /**
+     * HIS澧為噺鍚屾缁撴潫鏃堕棿锛堝搴� healthy_user.last_Update_Time锛�
+     */
+    @ApiModelProperty("鍚屾缁撴潫鏃堕棿锛屾牸寮忥細yyyy-MM-dd HH:mm:ss")
+    private String lastEndTime;
+
 
     public SysUser() {
 
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java
index 605ebc8..5ea0792 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java
@@ -4,7 +4,9 @@
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.DataAccessException;
 import org.springframework.data.redis.connection.DataType;
 import org.springframework.data.redis.core.*;
 import org.springframework.stereotype.Component;
@@ -14,6 +16,7 @@
  *
  * @author ruoyi
  **/
+@Slf4j
 @SuppressWarnings(value = {"unchecked", "rawtypes"})
 @Component
 public class RedisCache {
@@ -306,9 +309,25 @@
      * @return Long 澶т簬0琛ㄧず鍒犻櫎浜嗗嚑涓紝绛変簬0琛ㄧず娌℃湁鍒犻櫎锛屽皬浜�0琛ㄧず鍦ㄦ墽琛屽垹闄ゆ搷浣滄椂鍙戠敓浜嗛敊璇�
      */
     public Long removeElementFromList(String key, String valueToRemove) {
-        ListOperations<String, String> listOps = redisTemplate.opsForList();
-        Long remove = listOps.remove(key, 0, valueToRemove);
-        return remove;
+        // 鍏堝垽鏂� key 鐨勭被鍨嬶紝閬垮厤 key 琚叾瀹冧笟鍔�/鏃х増鏈唬鐮佸啓鎴愪簡 String銆丠ash 绛夐潪 List 绫诲瀷鏃舵姏 WRONGTYPE
+        DataType keyType = redisTemplate.type(key);
+        if (keyType == DataType.NONE) {
+            return 0L;
+        }
+        if (keyType != DataType.LIST) {
+            // 璇� key 鏈韩鏄綔涓洪槦鍒椾娇鐢ㄧ殑锛岀被鍨嬩笉瀵硅鏄庢槸鑴忔暟鎹紝鐩存帴鍒犻櫎锛岀瓑涓嬫鍐欏叆鏃堕噸鏂板垱寤轰负 List
+            log.warn("Redis key [{}] 鐨勭被鍨嬩负 {}锛屼笉鏄� List锛屽凡鍒犻櫎璇ヨ剰鏁版嵁锛岄伩鍏� WRONGTYPE 寮傚父", key, keyType);
+            redisTemplate.delete(key);
+            return 0L;
+        }
+        try {
+            Long remove = redisTemplate.opsForList().remove(key, 0, valueToRemove);
+            return remove == null ? 0L : remove;
+        } catch (DataAccessException e) {
+            // 绫诲瀷妫�鏌ュ拰鍒犻櫎涔嬮棿 key 琚叾瀹冨簲鐢ㄦ敼鍐欎负鍏跺畠绫诲瀷绛夋瀬绔儏鍐靛厹搴曪紝缂撳瓨娓呯悊澶辫触涓嶅簲褰卞搷涓讳笟鍔℃祦绋�
+            log.warn("浠� Redis List [{}] 绉婚櫎鍏冪礌 [{}] 澶辫触锛歿}", key, valueToRemove, e.getMessage());
+            return 0L;
+        }
     }
 
     /**
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/HttpUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/HttpUtil.java
index 971ef13..2c73634 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/HttpUtil.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/HttpUtil.java
@@ -95,7 +95,26 @@
         return httpClient;
     }
 
+    /**
+     * json鐨刾ost璇锋眰锛堝彧鍏冲績鏄惁鎴愬姛锛岄潪200鐩存帴鎶涘紓甯革級
+     *
+     * @param url     璇锋眰url
+     * @param request 璇锋眰鎶ユ枃
+     * @return 鍥哄畾杩斿洖true锛坔ttp鐘舵�侀潪200浼氭姏HttpRequestException锛�
+     */
     public static Boolean postJsonRequest(String url, String request) throws HttpRequestException {
+        postJsonRequestForString(url, request);
+        return true;
+    }
+
+    /**
+     * json鐨刾ost璇锋眰锛岃繑鍥炲搷搴旀姤鏂囧唴瀹癸紙鐢ㄤ簬闇�瑕佽В鏋愯繑鍥炵爜鐨勫満鏅紝濡備簯MAS鐭俊锛�
+     *
+     * @param url     璇锋眰url
+     * @param request 璇锋眰鎶ユ枃
+     * @return 鍝嶅簲鎶ユ枃鍐呭
+     */
+    public static String postJsonRequestForString(String url, String request) throws HttpRequestException {
         Assert.hasLength(url, "璇锋眰url涓嶈兘涓虹┖瀛楃涓层��");
         EntityEnclosingMethod httpMethod = new PostMethod(url);
 
@@ -132,8 +151,9 @@
             String respJson = stringBuffer.toString();
             // String respJson = httpMethod.getResponseBodyAsString();
             if (resultCode == OK) {
-                return true;
+                return respJson;
             } else {
+                log.warn("銆恜ostJsonRequestForString銆戣姹傚け璐ワ紝url锛歿}锛岀姸鎬佺爜锛歿}锛屽搷搴旓細{}", url, resultCode, respJson);
                 throw new HttpRequestException(resultCode, respJson);
             }
         } catch (UnsupportedEncodingException e) {
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/TreeUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/TreeUtils.java
index 873610d..aac16ca 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/TreeUtils.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/TreeUtils.java
@@ -76,13 +76,41 @@
      * 鏍规嵁 SysDept.parentId 鏋勯�犻儴闂ㄦ爲
      */
     public static List<SysDept> buildDeptTree(List<SysDept> depts) {
+        // 鎸� deptId 鍘婚噸锛岄槻姝� sys_user_dept 涓�瀵瑰鍏宠仈浜х敓閲嶅鐨勭埗鑺傜偣锛屽鑷存瀯寤洪儴闂ㄦ爲鏃跺嚭鐜伴棶棰�
+        depts = distinctByDeptId(depts);
         return buildTree(depts, SysDept::getDeptId, SysDept::getParentId, SysDept::setChildren);
     }
 
     /**
-     * 涓�娆℃�ц幏鍙栨墍鏈夐儴闂ㄥ彾瀛愯妭鐐�
+     * 浠庢墎骞抽儴闂ㄥ垪琛ㄤ竴娆℃�ц幏鍙栨墍鏈夊彾瀛愰儴闂細鍗炽�宒eptId 涓嶅嚭鐜板湪浠讳綍鑺傜偣鐨� parentId 涓�嶇殑鑺傜偣銆�
+     * 鍏ュ弬搴斾负鎵佸钩鍒楄〃锛堜笉鏄� buildDeptTree 鐨勮繑鍥炲�硷級锛屾棤闇�鍏堝缓鏍戙��
+     * 浼氬厛鎸� deptId 鍘婚噸锛岄伩鍏� join 浜х敓鐨勯噸澶嶉儴闂ㄥ彉鎴愰噸澶嶅彾瀛愩��
      */
     public static List<SysDept> getLeafDepts(List<SysDept> depts) {
-        return getLeafNodes(depts, SysDept::getHisDeptId, SysDept::getHisParentId);
+        return getLeafNodes(distinctByDeptId(depts), SysDept::getDeptId, SysDept::getParentId);
+    }
+
+    /**
+     * 鎸� deptId 瀵归儴闂ㄥ垪琛ㄥ幓閲嶏紝淇濈暀棣栨鍑虹幇鐨勮妭鐐广��
+     * 鏌ヨ缁撴灉閫氳繃 dept_code 鍏宠仈 sys_user_dept锛屽悓涓�閮ㄩ棬鍙兘鍑虹幇澶氭潯璁板綍锛堥噸澶嶇殑鐖惰妭鐐癸級锛�
+     * 鏋勫缓閮ㄩ棬鏍戝墠闇�鍘婚噸锛岄伩鍏嶉噸澶嶈妭鐐瑰鑷存爲缁撴瀯寮傚父銆�
+     *
+     * @param list 閮ㄩ棬鍒楄〃锛屽厑璁镐负 null
+     * @return 鍘婚噸鍚庣殑閮ㄩ棬鍒楄〃锛涘叆鍙備负 null 鎴栫┖鏃跺師鏍疯繑鍥�
+     */
+    private static List<SysDept> distinctByDeptId(List<SysDept> list) {
+        if (list == null || list.isEmpty()) {
+            return list;
+        }
+        Set<Long> deptIdSet = new HashSet<>();
+        List<SysDept> distinctList = new ArrayList<>(list.size());
+        for (SysDept dept : list) {
+            Long deptId = dept.getDeptId();
+            // deptId 涓虹┖鏃朵繚鐣欙紝閬垮厤璇悎骞朵笉鍚岄儴闂�
+            if (deptId == null || deptIdSet.add(deptId)) {
+                distinctList.add(dept);
+            }
+        }
+        return distinctList;
     }
 }
\ No newline at end of file
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/sms/SmsUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sms/SmsUtils.java
new file mode 100644
index 0000000..5390759
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sms/SmsUtils.java
@@ -0,0 +1,911 @@
+package com.ruoyi.common.utils.sms;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.annotation.JSONField;
+import com.ruoyi.common.utils.HttpUtil;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.http.HttpUtils;
+import com.ruoyi.common.utils.sign.Md5Utils;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 鐭俊鍙戦�佸伐鍏风被
+ *
+ * @author ruoyi
+ */
+@Slf4j
+public final class SmsUtils {
+
+    private SmsUtils() {
+    }
+
+    /**
+     * 浜慚AS 姣忔壒娆℃渶澶氭墜鏈哄彿鏁伴噺
+     */
+    private static final int YUNMAS_MAX_MOBILES = 5000;
+
+    /**
+     * 浜慚AS 杩斿洖鐮佽鏄�
+     */
+    private static final Map<String, String> YUNMAS_RSPCOD_DESC = new HashMap<>();
+
+    static {
+        YUNMAS_RSPCOD_DESC.put("success", "鏁版嵁楠岃瘉閫氳繃");
+        YUNMAS_RSPCOD_DESC.put("IllegalMac", "mac鏍¢獙涓嶉�氳繃");
+        YUNMAS_RSPCOD_DESC.put("IllegalSignId", "鏃犳晥鐨勭鍚嶇紪鐮�");
+        YUNMAS_RSPCOD_DESC.put("InvalidMessage", "闈炴硶娑堟伅锛岃姹傛暟鎹В鏋愬け璐�");
+        YUNMAS_RSPCOD_DESC.put("InvalidUsrOrPwd", "闈炴硶鐢ㄦ埛鍚�/瀵嗙爜");
+        YUNMAS_RSPCOD_DESC.put("NoSignId", "鏈尮閰嶅埌瀵瑰簲鐨勭鍚嶄俊鎭�");
+        YUNMAS_RSPCOD_DESC.put("TooManyMobiles", "鎵嬫満鍙锋暟閲忚秴闄�(>5000)");
+    }
+
+    /**
+     * 浜慚AS 鎺ュ彛閰嶇疆銆�
+     * <p>
+     * 娉ㄦ剰锛歴ecretKey 鍙弬涓� mac 璁$畻锛屼笉鑳芥斁杩涜姹傛姤鏂囥��
+     */
+    @Data
+    public static class YunMasConfig {
+        /**
+         * 鎺ュ彛鍦板潃
+         */
+        private String url;
+        /**
+         * 浼佷笟鍚嶇О
+         */
+        private String ecName;
+        /**
+         * 鎺ュ彛璐﹀彿鐢ㄦ埛鍚�
+         */
+        private String apId;
+        /**
+         * 瀵嗛挜锛堜粎鍙備笌 mac 璁$畻锛�
+         */
+        private String secretKey;
+        /**
+         * 绛惧悕缂栫爜
+         */
+        private String sign;
+        /**
+         * 鎵╁睍鐮侊細绮剧‘鍖归厤濉┖瀛楃涓诧紱妯$硦鍖归厤鍙~绌哄瓧绗︿覆鎴栬嚜瀹氫箟鎵╁睍鐮侊紙涓庢湇鍔′唬鐮佸悎璁′笉瓒呰繃20浣嶏級
+         */
+        private String addSerial;
+
+        public String getAddSerial() {
+            return addSerial == null ? "" : addSerial;
+        }
+
+        /**
+         * 涓芥按濡囧辜榛樿閰嶇疆锛堟湭鍦� sys_config 涓厤缃椂浣跨敤锛�
+         */
+        public static YunMasConfig defaultLishui() {
+            YunMasConfig config = new YunMasConfig();
+            config.setUrl("http://9.208.5.35:1992/sms/norsubmit");
+            config.setEcName("涓芥按甯傚骞间繚鍋ラ櫌");
+            config.setApId("dxjkkf");
+            config.setSecretKey("1q2w3e4r5t6y");
+            config.setSign("D9gKbwdt7");
+            config.setAddSerial("");
+            return config;
+        }
+
+        /**
+         * 鏍规嵁閰嶇疆鍐呭锛坖son锛夋瀯寤猴紝鏈厤缃殑瀛楁娌跨敤榛樿鍊�
+         *
+         * @param configJson 褰㈠ {"url":"...","ecName":"...","apId":"...","secretKey":"...","sign":"...","addSerial":""}
+         */
+        public static YunMasConfig of(String configJson) {
+            YunMasConfig config = defaultLishui();
+            if (StringUtils.isEmpty(configJson)) {
+                return config;
+            }
+            JSONObject json;
+            try {
+                json = JSON.parseObject(configJson);
+            } catch (Exception e) {
+                log.error("銆愪簯MAS銆戦厤缃В鏋愬け璐ワ紝浣跨敤榛樿閰嶇疆锛岄厤缃唴瀹癸細{}", configJson, e);
+                return config;
+            }
+            if (json == null) {
+                return config;
+            }
+            if (StringUtils.isNotEmpty(json.getString("url"))) {
+                config.setUrl(json.getString("url"));
+            }
+            if (StringUtils.isNotEmpty(json.getString("ecName"))) {
+                config.setEcName(json.getString("ecName"));
+            }
+            if (StringUtils.isNotEmpty(json.getString("apId"))) {
+                config.setApId(json.getString("apId"));
+            }
+            if (StringUtils.isNotEmpty(json.getString("secretKey"))) {
+                config.setSecretKey(json.getString("secretKey"));
+            }
+            if (StringUtils.isNotEmpty(json.getString("sign"))) {
+                config.setSign(json.getString("sign"));
+            }
+            if (json.containsKey("addSerial")) {
+                config.setAddSerial(json.getString("addSerial"));
+            }
+            return config;
+        }
+
+        /**
+         * 鍏抽敭鍙傛暟鏄惁榻愬叏
+         */
+        public boolean isValid() {
+            return StringUtils.isNotEmpty(url) && StringUtils.isNotEmpty(ecName)
+                    && StringUtils.isNotEmpty(apId) && StringUtils.isNotEmpty(secretKey)
+                    && StringUtils.isNotEmpty(sign);
+        }
+    }
+
+    /**
+     * 鐭俊鍙戦�佺粨鏋�
+     */
+    @Data
+    public static class SmsResult {
+        /**
+         * 骞冲彴杩斿洖鐨勭姸鎬佺爜锛坰uccess 琛ㄧず鏍¢獙閫氳繃锛�
+         */
+        private String rspcod;
+        /**
+         * 娑堟伅鎵规鍙凤紝澶辫触鏃朵负绌�
+         */
+        private String msgGroup;
+        /**
+         * 骞冲彴杩斿洖鐨勬暟鎹牎楠岀粨鏋�
+         */
+        private boolean success;
+        /**
+         * 鏈湴寮傚父/澶辫触鎻忚堪锛屼究浜庢帓鏌�
+         */
+        private String message;
+
+        /**
+         * 鏄惁鍙戦�佹垚鍔燂紙骞冲彴鏍¢獙閫氳繃锛�
+         */
+        @JSONField(serialize = false, deserialize = false)
+        public boolean isOk() {
+            return success && "success".equals(rspcod);
+        }
+
+        public static SmsResult fail(String message) {
+            SmsResult result = new SmsResult();
+            result.setSuccess(false);
+            result.setMessage(message);
+            return result;
+        }
+    }
+
+    /**
+     * 鏉窞甯備竴鍖婚櫌鍚村北闄㈠尯鎴戠煭淇℃帴鍙�
+     *
+     * @param url      鐭俊URL
+     * @param userName 鐭俊甯愬彿
+     * @param passWord 鐭俊瀵嗙爜
+     * @param phone    鎵嬫満鍙风粍鍚堝 13500000001,13500000002
+     * @param content  鐭俊鍐呭
+     * @return 鍙戦�佺粨鏋�
+     */
+    public static String sendSms(String url, String userName, String passWord, String phone, String content) {
+        long timestamp = new Date().getTime();
+        String sign = Md5Utils.hash(userName + timestamp + Md5Utils.hash(passWord));
+        content = "銆愭澀宸炲競鑲跨槫鍖婚櫌銆�" + content;
+        String jsonMsg = "{" +
+                "\"userName\": \"" + userName + "\"," +
+                "\"content\": \"" + content + "\"," +
+                "\"phoneList\": [" + phone + "]," +
+                "\"timestamp\": " + timestamp + "," +
+                "\"sign\": \"" + sign + "\"" +
+                "}";
+        Boolean sendMsg = HttpUtil.postJsonRequest(url, jsonMsg);
+        return sendMsg.toString();
+
+    }
+
+
+    /**
+     * 鏉窞甯備竴鍖婚櫌婀栨花闄㈠尯鎴戠煭淇℃帴鍙�
+     *
+     * @param url      鐭俊URL
+     * @param userName 鐭俊甯愬彿
+     * @param passWord 鐭俊瀵嗙爜
+     * @param phone    鎵嬫満鍙风粍鍚堝 13500000001,13500000002
+     * @param content  鐭俊鍐呭
+     * @return 鍙戦�佺粨鏋�
+     */
+    public static String sendSms1(String url, String userName, String passWord, String phone, String content) {
+        long timestamp = new Date().getTime();
+        String sign = Md5Utils.hash(userName + timestamp + Md5Utils.hash(passWord));
+        content = "銆愭澀宸炲競绗竴浜烘皯鍖婚櫌銆�" + content;
+        String jsonMsg = "{" +
+                "\"userName\": \"" + userName + "\"," +
+                "\"content\": \"" + content + "\"," +
+                "\"phoneList\": [" + phone + "]," +
+                "\"timestamp\": " + timestamp + "," +
+                "\"sign\": \"" + sign + "\"" +
+                "}";
+        Boolean sendMsg = HttpUtil.postJsonRequest(url, jsonMsg);
+        return sendMsg.toString();
+
+    }
+
+    /**
+     * 涓浗绉诲姩浜慚AS 鏅�氱煭淇″彂閫侊紙涓�瀵逛竴/澶氬涓�妯″紡锛�
+     * <p>
+     * 鎺ュ彛锛歅OST {url}锛屾姤鏂囦负 json 鐨� base64 缂栫爜銆�
+     * <ul>
+     *     <li>mac 鎸� ecName銆乤pId銆乻ecretKey銆乵obiles銆乧ontent銆乻ign銆乤ddSerial 椤哄簭鎷兼帴鍚庡彇 MD5锛�32浣嶅皬鍐欙級</li>
+     *     <li>secretKey 鍙弬涓� mac 璁$畻锛屼笉鏀捐繘璇锋眰鎶ユ枃</li>
+     *     <li>绛惧悕鐢卞钩鍙版牴鎹� sign 缂栫爜鑷姩杩藉姞锛宑ontent 涓笉瑕佸啀鎵嬪伐鎷笺�愮鍚嶃��</li>
+     * </ul>
+     *
+     * @param config  浜慚AS 閰嶇疆
+     * @param mobiles 鏀朵俊鎵嬫満鍙凤紝鑻辨枃閫楀彿鍒嗛殧锛屽 13800138000,13800138001锛屾瘡鎵逛笉瓒呰繃 5000 涓�
+     * @param content 鐭俊鍐呭锛坢ac 鐢ㄦ渶缁堝唴瀹硅绠楋紝璁$畻鍚庝笉寰楀啀淇敼锛�
+     * @return 鍙戦�佺粨鏋滐紝閫氳繃 {@link SmsResult#isOk()} 鍒ゆ柇鏄惁鎴愬姛
+     */
+    public static SmsResult sendYunMasSms(YunMasConfig config, String mobiles, String content) {
+        if (config == null || !config.isValid()) {
+            return SmsResult.fail("浜慚AS閰嶇疆涓嶅畬鏁�");
+        }
+        if (StringUtils.isEmpty(mobiles)) {
+            return SmsResult.fail("鏀朵俊鎵嬫満鍙蜂笉鑳戒负绌�");
+        }
+        if (StringUtils.isEmpty(content)) {
+            return SmsResult.fail("鐭俊鍐呭涓嶈兘涓虹┖");
+        }
+        if (mobiles.split(",").length > YUNMAS_MAX_MOBILES) {
+            return SmsResult.fail("鎵嬫満鍙锋暟閲忚秴闄�(>" + YUNMAS_MAX_MOBILES + ")");
+        }
+
+        String addSerial = config.getAddSerial();
+        // 1. mac 蹇呴』鐢ㄦ姤鏂囬噷鏈�缁堝彂閫佺殑鍐呭璁$畻
+        String mac = Md5Utils.hash(config.getEcName() + config.getApId() + config.getSecretKey()
+                + mobiles + content + config.getSign() + addSerial);
+
+        // 2. 鐢� json 缁勮鎶ユ枃锛岄伩鍏嶅唴瀹逛腑鐨勫紩鍙�/鍙嶆枩鏉犵牬鍧忔姤鏂囷紱secretKey 涓嶈惤鎶ユ枃
+        JSONObject body = new JSONObject(true);
+        body.put("ecName", config.getEcName());
+        body.put("apId", config.getApId());
+        body.put("mobiles", mobiles);
+        body.put("content", content);
+        body.put("sign", config.getSign());
+        body.put("addSerial", addSerial);
+        body.put("mac", mac);
+
+        String requestBody = Base64.getEncoder()
+                .encodeToString(body.toJSONString().getBytes(StandardCharsets.UTF_8));
+
+        try {
+            String resp = HttpUtil.postJsonRequestForString(config.getUrl(), requestBody);
+            log.info("銆愪簯MAS銆戝彂閫佺煭淇★紝鎵嬫満鍙凤細{}锛屽搷搴旓細{}", mobiles, resp);
+            if (StringUtils.isEmpty(resp)) {
+                return SmsResult.fail("浜慚AS鍝嶅簲涓虹┖");
+            }
+            SmsResult result = JSON.parseObject(resp, SmsResult.class);
+            if (result == null) {
+                return SmsResult.fail("浜慚AS鍝嶅簲瑙f瀽澶辫触锛�" + resp);
+            }
+            if (!result.isOk()) {
+                String desc = YUNMAS_RSPCOD_DESC.getOrDefault(result.getRspcod(), "鏈煡閿欒");
+                result.setMessage(desc);
+                log.error("銆愪簯MAS銆戝彂閫佸け璐ワ紝鎵嬫満鍙凤細{}锛宺spcod锛歿}锛坽}锛夛紝鍝嶅簲锛歿}",
+                        mobiles, result.getRspcod(), desc, resp);
+            }
+            return result;
+        } catch (Exception e) {
+            log.error("銆愪簯MAS銆戝彂閫佸紓甯革紝鎵嬫満鍙凤細{}锛屽師鍥狅細{}", mobiles, e.getMessage(), e);
+            return SmsResult.fail("浜慚AS鍙戦�佸紓甯革細" + e.getMessage());
+        }
+    }
+
+    /**
+     * 鏉窞甯備竴鍖婚櫌鍚村北闄㈠尯寰俊鎺ュ彛
+     *
+     * @param url    寰俊URL
+     * @param phone  鎵嬫満鍙� 13500000001
+     * @param idCard 韬唤璇�
+     * @return 鍙戦�佺粨鏋�
+     */
+    public static String sendChat(String url, String phone, String idCard) {
+        String urlTemp = "http://192.200.54.14:5003/sfjk/SendMessage?sfzh="
+                + idCard
+                + "&title=鎮ㄥソ锛岄個璇锋偍濉啓鍑洪櫌璋冩煡琛紝璇风偣鍑诲~鍐欍��&content=寰呴殢璁�&phone=" + phone
+                + "&url=" + url + "&key=ff76f8904f5f32b5ee1739e8ea46e60g";
+        String sendMsg = "";
+        try {
+            log.info("銆恠endChat銆戝競涓�寰俊鍏紬鍙疯姹傚弬鏁帮細{}", urlTemp);
+            sendMsg = HttpUtils.sendPost(urlTemp);
+            log.info("銆恠endChat銆戝競涓�寰俊鍏紬鍙疯繑鍥炲�硷細{}", sendMsg);
+        } catch (Exception ex) {
+            sendMsg = ex.getMessage();
+            log.error(ex.getMessage());
+        }
+        return sendMsg;
+    }
+
+    /**
+     * 涓芥按寰俊 app-api 鍓嶇紑
+     */
+    private static final String LS_WX_APP_PREFIX = "/app-api";
+
+    /**
+     * 涓芥按寰俊寮�鏀炬帴鍙e墠缂�
+     */
+    private static final String LS_WX_API_PREFIX = LS_WX_APP_PREFIX + "/cross/open-api";
+
+    /**
+     * 鑾峰彇璁块棶浠ょ墝
+     */
+    private static final String LS_WX_TOKEN_PATH = "/token";
+
+    /**
+     * 鑾峰彇娑堟伅妯℃澘
+     */
+    private static final String LS_WX_TEMPLATE_PATH = "/getTemplate";
+
+    /**
+     * 鍙戦�佹秷鎭�
+     */
+    private static final String LS_WX_SEND_PATH = "/sendMessage";
+
+    /**
+     * 璁块棶浠ょ墝璇锋眰澶�
+     */
+    private static final String LS_WX_TOKEN_HEADER = "openApiToken";
+
+    /**
+     * 浠ょ墝缂撳瓨锛坘ey锛歜aseUrl|appId锛�
+     */
+    private static final Map<String, LsWxToken> LS_WX_TOKEN_CACHE = new ConcurrentHashMap<>();
+
+    /**
+     * 浠ょ墝鎻愬墠鍒锋柊鐨勭鏁帮紝閬垮厤涓寸晫杩囨湡
+     */
+    private static final long LS_WX_TOKEN_REFRESH_AHEAD_SECONDS = 60L;
+
+    /**
+     * 涓芥按寰俊鎺ュ彛閰嶇疆
+     */
+    @Data
+    public static class LsWxConfig {
+        /**
+         * 搴旂敤鍚嶇О
+         */
+        private String appName;
+        /**
+         * 鎺ュ彛鏍瑰湴鍧�锛屽 http://127.0.0.1:8080
+         * <p>
+         * 鍙互鍙~ scheme://host:port锛屼篃鍙互濉埌 /app-api 鎴� /app-api/cross/open-api銆�
+         */
+        private String baseUrl;
+        /**
+         * 搴旂敤ID
+         */
+        private String appId;
+        /**
+         * 搴旂敤瀵嗛挜
+         */
+        private String appSecret;
+        /**
+         * 榛樿娑堟伅妯℃澘ID锛屾秷鎭噷鏈寚瀹氭椂浣跨敤
+         */
+        private Long templateId;
+        /**
+         * 榛樿娑堟伅妯℃澘鍚嶇О锛屾湭閰嶇疆妯℃澘ID鏃堕�氳繃 getTemplate 鎸夊悕绉版煡鎵�
+         */
+        private String templateName;
+        /**
+         * 杩炴帴瓒呮椂锛屽崟浣嶏細姣
+         */
+        private Integer connectTimeout = 5000;
+        /**
+         * 璇诲彇瓒呮椂锛屽崟浣嶏細姣
+         */
+        private Integer readTimeout = 10000;
+
+        /**
+         * 涓芥按榛樿閰嶇疆
+         */
+        public static LsWxConfig defaultLishui() {
+            LsWxConfig config = new LsWxConfig();
+            config.setAppName("鎮h�呴殢璁�");
+            config.setBaseUrl("https://wmini.lsfby.com.cn/app-api");
+            config.setAppId("kpzEHgZSteCO4lz74");
+            config.setAppSecret("HKUDVMJSVTKQADIXV7XXRLNBQM8HNNL7");
+            // 娑堟伅妯℃澘锛氳瘖鍚庨殢璁挎彁閱掞紙鏈寚瀹氭ā鏉縄D鏃舵寜鍚嶇О浠� getTemplate 鏌ヨ锛�
+            config.setTemplateName("璇婂悗闅忚鎻愰啋");
+            return config;
+        }
+
+        /**
+         * 鏍规嵁閰嶇疆鍐呭锛坖son锛夋瀯寤猴紝鏈厤缃殑瀛楁娌跨敤榛樿鍊�
+         *
+         * @param configJson 褰㈠
+         *                   {"appName":"鎮h�呴殢璁�","baseUrl":"https://wmini.lsfby.com.cn/app-api","appId":"...","appSecret":"...","templateId":16,"templateName":"璇婂悗闅忚鎻愰啋"}
+         */
+        public static LsWxConfig of(String configJson) {
+            LsWxConfig config = defaultLishui();
+            if (StringUtils.isEmpty(configJson)) {
+                return config;
+            }
+            JSONObject json;
+            try {
+                json = JSON.parseObject(configJson);
+            } catch (Exception e) {
+                log.error("銆愪附姘村井淇°�戦厤缃В鏋愬け璐ワ紝浣跨敤榛樿閰嶇疆锛岄厤缃唴瀹癸細{}", configJson, e);
+                return config;
+            }
+            if (json == null) {
+                return config;
+            }
+            if (StringUtils.isNotEmpty(json.getString("appName"))) {
+                config.setAppName(json.getString("appName"));
+            }
+            if (StringUtils.isNotEmpty(json.getString("baseUrl"))) {
+                config.setBaseUrl(json.getString("baseUrl"));
+            } else if (StringUtils.isNotEmpty(json.getString("url"))) {
+                config.setBaseUrl(json.getString("url"));
+            } else if (StringUtils.isNotEmpty(json.getString("address"))) {
+                config.setBaseUrl(json.getString("address"));
+            }
+            if (StringUtils.isNotEmpty(json.getString("appId"))) {
+                config.setAppId(json.getString("appId"));
+            }
+            if (StringUtils.isNotEmpty(json.getString("appSecret"))) {
+                config.setAppSecret(json.getString("appSecret"));
+            }
+            if (json.containsKey("templateId")) {
+                config.setTemplateId(json.getLong("templateId"));
+            } else if (json.containsKey("id")) {
+                config.setTemplateId(json.getLong("id"));
+            }
+            if (StringUtils.isNotEmpty(json.getString("templateName"))) {
+                config.setTemplateName(json.getString("templateName"));
+            } else if (StringUtils.isNotEmpty(json.getString("tmpName"))) {
+                config.setTemplateName(json.getString("tmpName"));
+            }
+            if (json.containsKey("connectTimeout")) {
+                config.setConnectTimeout(json.getInteger("connectTimeout"));
+            }
+            if (json.containsKey("readTimeout")) {
+                config.setReadTimeout(json.getInteger("readTimeout"));
+            }
+            return config;
+        }
+
+        /**
+         * 鍏抽敭鍙傛暟鏄惁榻愬叏
+         */
+        public boolean isValid() {
+            return StringUtils.isNotEmpty(baseUrl) && StringUtils.isNotEmpty(appId)
+                    && StringUtils.isNotEmpty(appSecret);
+        }
+
+        /**
+         * 鎷兼帴寮�鏀炬帴鍙e湴鍧�
+         */
+        private String url(String path) {
+            String prefix = baseUrl == null ? "" : baseUrl.trim();
+            while (prefix.endsWith("/")) {
+                prefix = prefix.substring(0, prefix.length() - 1);
+            }
+            if (prefix.endsWith(LS_WX_API_PREFIX)) {
+                return prefix + path;
+            }
+            if (prefix.endsWith(LS_WX_APP_PREFIX)) {
+                return prefix + "/cross/open-api" + path;
+            }
+            return prefix + LS_WX_API_PREFIX + path;
+        }
+    }
+
+    /**
+     * 涓芥按寰俊娑堟伅鍙傛暟锛屽瓧娈典笌 sendMessage 鎺ュ彛涓�鑷�
+     */
+    @Data
+    public static class LsWxMessage {
+        /**
+         * 娑堟伅妯℃澘ID锛屼负绌烘椂浣跨敤閰嶇疆涓殑妯℃澘
+         */
+        private Long id;
+        /**
+         * 鎮h�呯紪鍙�
+         */
+        private String patientId;
+        /**
+         * 鎮h�呭鍚�
+         */
+        private String patientName;
+        /**
+         * 璇佷欢鍙�
+         */
+        private String idCard;
+        /**
+         * 鎵嬫満鍙风爜
+         */
+        private String mobile;
+        /**
+         * 灏辫瘖鍗″彿
+         */
+        private String medCardNo;
+        /**
+         * 閫氱煡鏍囬
+         */
+        private String titleName;
+        /**
+         * 閫氱煡鍐呭
+         */
+        private String content;
+        /**
+         * 閾炬帴鍦板潃
+         */
+        private String url;
+    }
+
+    /**
+     * 璁块棶浠ょ墝缂撳瓨瀵硅薄
+     */
+    private static class LsWxToken {
+        private final String token;
+        private final long expireAt;
+
+        private LsWxToken(String token, long expireAt) {
+            this.token = token;
+            this.expireAt = expireAt;
+        }
+
+        private String getToken() {
+            return token;
+        }
+
+        private long getExpireAt() {
+            return expireAt;
+        }
+    }
+
+    /**
+     * 涓芥按寰俊娑堟伅鍙戦�侊紙鍏堣幏鍙�/澶嶇敤璁块棶浠ょ墝锛屽啀璋冪敤 sendMessage锛�
+     * <p>
+     * 鎺ュ彛娴佺▼锛�
+     * <ol>
+     *     <li>GET /app-api/cross/open-api/token?appId=...&appSecret=... 鑾峰彇 openApiToken</li>
+     *     <li>POST /app-api/cross/open-api/sendMessage锛岃姹傚ご鎼哄甫 openApiToken</li>
+     * </ol>
+     *
+     * @param config  涓芥按寰俊閰嶇疆
+     * @param message 娑堟伅鍙傛暟
+     * @return 鎺ュ彛鍘熷鍝嶅簲锛涙湰鍦版牎楠屽け璐ユ椂杩斿洖 {"code":-1,"msg":"澶辫触鍘熷洜"}
+     */
+    public static String sendChatLs(LsWxConfig config, LsWxMessage message) {
+        if (config == null || !config.isValid()) {
+            return lsWxFail("涓芥按寰俊閰嶇疆涓嶅畬鏁达紝璇烽厤缃� baseUrl銆乤ppId銆乤ppSecret");
+        }
+        if (message == null) {
+            return lsWxFail("涓芥按寰俊娑堟伅鍙傛暟涓嶈兘涓虹┖");
+        }
+        if (StringUtils.isEmpty(message.getMobile()) && StringUtils.isEmpty(message.getIdCard())
+                && StringUtils.isEmpty(message.getPatientId()) && StringUtils.isEmpty(message.getMedCardNo())) {
+            return lsWxFail("鎵嬫満鍙风爜銆佽瘉浠跺彿銆佹偅鑰呯紪鍙枫�佸氨璇婂崱鍙蜂笉鑳藉叏閮ㄤ负绌�");
+        }
+        try {
+            String token = getLsWxToken(config);
+            if (StringUtils.isEmpty(token)) {
+                return lsWxFail("鑾峰彇涓芥按寰俊璁块棶浠ょ墝澶辫触");
+            }
+
+            // 妯℃澘浼樺厛绾э細娑堟伅鎸囧畾 > 閰嶇疆鎸囧畾 > 鎸夋ā鏉垮悕绉版煡璇�
+            Long templateId = message.getId();
+            if (templateId == null) {
+                templateId = config.getTemplateId();
+            }
+            if (templateId == null) {
+                templateId = findLsWxTemplateId(config, token, config.getTemplateName());
+                if (templateId == null && StringUtils.isNotEmpty(config.getTemplateName())) {
+                    // 鍙兘鏄紦瀛樼殑浠ょ墝宸插け鏁堬紝鍒锋柊浠ょ墝鍚庨噸璇曚竴娆�
+                    LS_WX_TOKEN_CACHE.remove(tokenCacheKey(config));
+                    token = getLsWxToken(config);
+                    if (StringUtils.isNotEmpty(token)) {
+                        templateId = findLsWxTemplateId(config, token, config.getTemplateName());
+                    }
+                }
+            }
+            if (templateId == null) {
+                return lsWxFail("鏈壘鍒颁附姘村井淇℃秷鎭ā鏉匡紝璇锋寚瀹氭秷鎭ā鏉縄D鎴栨ā鏉垮悕绉�");
+            }
+
+            JSONObject body = new JSONObject(true);
+            body.put("id", templateId);
+            body.put("patientId", nvl(message.getPatientId()));
+            body.put("patientName", nvl(message.getPatientName()));
+            body.put("idCard", nvl(message.getIdCard()));
+            body.put("mobile", message.getMobile());
+            body.put("medCardNo", nvl(message.getMedCardNo()));
+            body.put("titleName", nvl(message.getTitleName()));
+            body.put("content", nvl(message.getContent()));
+            body.put("url", nvl(message.getUrl()));
+            String requestBody = body.toJSONString();
+
+            Map<String, String> headers = new HashMap<>(2);
+            headers.put(LS_WX_TOKEN_HEADER, token);
+            String sendUrl = config.url(LS_WX_SEND_PATH);
+            log.info("銆愪附姘村井淇°�戝彂閫佹秷鎭紝鍦板潃锛歿}锛屽弬鏁帮細{}", sendUrl, requestBody);
+            String resp = lsWxRequest("POST", sendUrl, requestBody, headers, config);
+            log.info("銆愪附姘村井淇°�戝彂閫佺粨鏋滐細{}", resp);
+
+            // 浠ょ墝澶辨晥鏃舵竻闄ょ紦瀛樺苟閲嶈瘯涓�娆�
+            if (isLsWxTokenInvalid(resp)) {
+                LS_WX_TOKEN_CACHE.remove(tokenCacheKey(config));
+                token = getLsWxToken(config);
+                if (StringUtils.isNotEmpty(token)) {
+                    headers.put(LS_WX_TOKEN_HEADER, token);
+                    resp = lsWxRequest("POST", sendUrl, requestBody, headers, config);
+                    log.info("銆愪附姘村井淇°�戜护鐗屽け鏁堥噸璇曠粨鏋滐細{}", resp);
+                }
+            }
+            return resp;
+        } catch (Exception e) {
+            log.error("銆愪附姘村井淇°�戝彂閫佸紓甯革紝鎵嬫満鍙凤細{}锛屽師鍥狅細{}", message.getMobile(), e.getMessage(), e);
+            return lsWxFail("涓芥按寰俊鍙戦�佸紓甯革細" + e.getMessage());
+        }
+    }
+
+    /**
+     * 涓芥按寰俊娑堟伅鍙戦�侊紙甯哥敤闅忚鍙傛暟锛�
+     *
+     * @param config      涓芥按寰俊閰嶇疆
+     * @param mobile      鎵嬫満鍙风爜
+     * @param idCard      璇佷欢鍙�
+     * @param patientName 鎮h�呭鍚�
+     * @param titleName   閫氱煡鏍囬
+     * @param content     閫氱煡鍐呭
+     * @param url         閾炬帴鍦板潃
+     * @return 鎺ュ彛鍘熷鍝嶅簲锛涙湰鍦版牎楠屽け璐ユ椂杩斿洖 {"code":-1,"msg":"澶辫触鍘熷洜"}
+     */
+    public static String sendChatLs(LsWxConfig config, String mobile, String idCard, String patientName,
+                                    String titleName, String content, String url) {
+        LsWxMessage message = new LsWxMessage();
+        message.setMobile(mobile);
+        message.setIdCard(idCard);
+        message.setPatientName(patientName);
+        message.setTitleName(titleName);
+        message.setContent(content);
+        message.setUrl(url);
+        return sendChatLs(config, message);
+    }
+
+    /**
+     * 涓芥按寰俊娑堟伅鍙戦�侊紙閰嶇疆鐩存帴浼� sys_config 涓殑 json锛�
+     *
+     * @param configJson  涓芥按寰俊閰嶇疆 json
+     * @param mobile      鎵嬫満鍙风爜
+     * @param idCard      璇佷欢鍙�
+     * @param patientName 鎮h�呭鍚�
+     * @param titleName   閫氱煡鏍囬
+     * @param content     閫氱煡鍐呭
+     * @param url         閾炬帴鍦板潃
+     * @return 鎺ュ彛鍘熷鍝嶅簲锛涙湰鍦版牎楠屽け璐ユ椂杩斿洖 {"code":-1,"msg":"澶辫触鍘熷洜"}
+     */
+    public static String sendChatLs(String configJson, String mobile, String idCard, String patientName,
+                                    String titleName, String content, String url) {
+        return sendChatLs(LsWxConfig.of(configJson), mobile, idCard, patientName, titleName, content, url);
+    }
+
+    /**
+     * 鑾峰彇涓芥按寰俊璁块棶浠ょ墝锛屼紭鍏堜娇鐢ㄧ紦瀛�
+     */
+    private static String getLsWxToken(LsWxConfig config) throws IOException {
+        String cacheKey = tokenCacheKey(config);
+        long now = System.currentTimeMillis();
+        LsWxToken cached = LS_WX_TOKEN_CACHE.get(cacheKey);
+        if (cached != null && cached.getExpireAt() > now) {
+            return cached.getToken();
+        }
+
+        String url = config.url(LS_WX_TOKEN_PATH)
+                + "?appId=" + encode(config.getAppId())
+                + "&appSecret=" + encode(config.getAppSecret());
+        String resp = lsWxRequest("GET", url, null, null, config);
+        log.info("銆愪附姘村井淇°�戣幏鍙杢oken鍝嶅簲锛歿}", resp);
+        if (StringUtils.isEmpty(resp)) {
+            return null;
+        }
+        JSONObject json = JSON.parseObject(resp);
+        if (json == null || json.getIntValue("code") != 0) {
+            log.error("銆愪附姘村井淇°�戣幏鍙杢oken澶辫触锛歿}", resp);
+            return null;
+        }
+        JSONObject data = json.getJSONObject("data");
+        String token = data == null ? null : data.getString("token");
+        if (StringUtils.isEmpty(token)) {
+            log.error("銆愪附姘村井淇°�戣幏鍙杢oken澶辫触锛屽搷搴斾腑娌℃湁token锛歿}", resp);
+            return null;
+        }
+
+        long expiresIn = data.getLongValue("expiresIn");
+        if (expiresIn <= 0) {
+            expiresIn = 3000L;
+        }
+        long expireAt = now + Math.max(expiresIn - LS_WX_TOKEN_REFRESH_AHEAD_SECONDS, 1L) * 1000L;
+        LS_WX_TOKEN_CACHE.put(cacheKey, new LsWxToken(token, expireAt));
+        return token;
+    }
+
+    /**
+     * 鏍规嵁妯℃澘鍚嶇О鏌ヨ妯℃澘ID
+     */
+    private static Long findLsWxTemplateId(LsWxConfig config, String token, String templateName) throws IOException {
+        if (StringUtils.isEmpty(templateName)) {
+            return null;
+        }
+        String url = config.url(LS_WX_TEMPLATE_PATH) + "?appId=" + encode(config.getAppId());
+        Map<String, String> headers = new HashMap<>(2);
+        headers.put(LS_WX_TOKEN_HEADER, token);
+        String resp = lsWxRequest("GET", url, null, headers, config);
+        log.info("銆愪附姘村井淇°�戣幏鍙栨ā鏉垮搷搴旓細{}", resp);
+        if (StringUtils.isEmpty(resp)) {
+            return null;
+        }
+        JSONObject json = JSON.parseObject(resp);
+        if (json == null || json.getIntValue("code") != 0) {
+            log.error("銆愪附姘村井淇°�戣幏鍙栨ā鏉垮け璐ワ細{}", resp);
+            return null;
+        }
+        JSONArray data = json.getJSONArray("data");
+        if (data == null) {
+            return null;
+        }
+        for (int i = 0; i < data.size(); i++) {
+            JSONObject item = data.getJSONObject(i);
+            if (item != null && templateName.equals(item.getString("tmpName"))) {
+                return item.getLong("id");
+            }
+        }
+        log.error("銆愪附姘村井淇°�戞湭鎵惧埌妯℃澘锛歿}", templateName);
+        return null;
+    }
+
+    /**
+     * 鍒ゆ柇鍝嶅簲鏄惁灞炰簬浠ょ墝澶辨晥
+     */
+    private static boolean isLsWxTokenInvalid(String resp) {
+        if (StringUtils.isEmpty(resp)) {
+            return false;
+        }
+        try {
+            JSONObject json = JSON.parseObject(resp);
+            if (json == null) {
+                return false;
+            }
+            int code = json.getIntValue("code");
+            if (code == 401 || code == 403) {
+                return true;
+            }
+            String msg = json.getString("msg");
+            if (StringUtils.isEmpty(msg)) {
+                return false;
+            }
+            String lower = msg.toLowerCase();
+            return (lower.contains("token") || msg.contains("浠ょ墝"))
+                    && (lower.contains("expire") || lower.contains("invalid")
+                    || msg.contains("澶辨晥") || msg.contains("杩囨湡"));
+        } catch (Exception e) {
+            return false;
+        }
+    }
+
+    /**
+     * 鍙戦�佷附姘村井淇� HTTP 璇锋眰
+     */
+    private static String lsWxRequest(String method, String url, String body,
+                                      Map<String, String> headers, LsWxConfig config) throws IOException {
+        HttpURLConnection connection = null;
+        try {
+            connection = (HttpURLConnection) new URL(url).openConnection();
+            connection.setRequestMethod(method);
+            connection.setConnectTimeout(config.getConnectTimeout() == null ? 5000 : config.getConnectTimeout());
+            connection.setReadTimeout(config.getReadTimeout() == null ? 10000 : config.getReadTimeout());
+            connection.setRequestProperty("Accept", "*/*");
+            if (headers != null) {
+                for (Map.Entry<String, String> entry : headers.entrySet()) {
+                    connection.setRequestProperty(entry.getKey(), entry.getValue());
+                }
+            }
+            if (body != null) {
+                connection.setDoOutput(true);
+                connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
+                try (OutputStream os = connection.getOutputStream()) {
+                    os.write(body.getBytes(StandardCharsets.UTF_8));
+                }
+            }
+
+            int status = connection.getResponseCode();
+            InputStream inputStream = status >= 200 && status < 300
+                    ? connection.getInputStream() : connection.getErrorStream();
+            String resp = readStream(inputStream);
+            if (status < 200 || status >= 300) {
+                log.warn("銆愪附姘村井淇°�戣姹傝繑鍥為潪200锛屽湴鍧�锛歿}锛岀姸鎬佺爜锛歿}锛屽搷搴旓細{}", url, status, resp);
+            }
+            return resp;
+        } finally {
+            if (connection != null) {
+                connection.disconnect();
+            }
+        }
+    }
+
+    /**
+     * 璇诲彇鍝嶅簲娴�
+     */
+    private static String readStream(InputStream inputStream) throws IOException {
+        if (inputStream == null) {
+            return "";
+        }
+        StringBuilder result = new StringBuilder();
+        try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
+            String line;
+            while ((line = reader.readLine()) != null) {
+                result.append(line);
+            }
+        }
+        return result.toString();
+    }
+
+    /**
+     * 浠ょ墝缂撳瓨key
+     */
+    private static String tokenCacheKey(LsWxConfig config) {
+        return config.getBaseUrl() + "|" + config.getAppId();
+    }
+
+    /**
+     * URL鍙傛暟缂栫爜
+     */
+    private static String encode(String value) {
+        if (value == null) {
+            return "";
+        }
+        try {
+            return URLEncoder.encode(value, StandardCharsets.UTF_8.name());
+        } catch (Exception e) {
+            return value;
+        }
+    }
+
+    /**
+     * null杞┖瀛楃涓�
+     */
+    private static String nvl(String value) {
+        return value == null ? "" : value;
+    }
+
+    /**
+     * 鏋勯�犳湰鍦板け璐ュ搷搴�
+     */
+    private static String lsWxFail(String msg) {
+        JSONObject json = new JSONObject(true);
+        json.put("code", -1);
+        json.put("msg", msg);
+        return json.toJSONString();
+    }
+}
diff --git a/ruoyi-patarchive/src/main/java/com/ruoyi/patarchive/controller/PatSpecificArchiveGenerateConfigController.java b/ruoyi-patarchive/src/main/java/com/ruoyi/patarchive/controller/PatSpecificArchiveGenerateConfigController.java
new file mode 100644
index 0000000..67e4b62
--- /dev/null
+++ b/ruoyi-patarchive/src/main/java/com/ruoyi/patarchive/controller/PatSpecificArchiveGenerateConfigController.java
@@ -0,0 +1,118 @@
+package com.ruoyi.patarchive.controller;
+
+import com.ruoyi.common.annotation.AddOrgId;
+import com.ruoyi.common.annotation.Log;
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.core.domain.model.LoginUser;
+import com.ruoyi.common.core.page.TableDataInfo;
+import com.ruoyi.common.enums.BusinessType;
+import com.ruoyi.common.utils.PageUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.patarchive.domain.PatSpecificArchiveGenerateConfig;
+import com.ruoyi.patarchive.service.IPatSpecificArchiveGenerateConfigService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+/**
+ * 涓撶妗f鐢熸垚閰嶇疆Controller
+ *
+ * @author ruoyi
+ * @date 2026-10-12
+ */
+@Api(description = "涓撶妗f鐢熸垚閰嶇疆")
+@RestController
+@RequestMapping("/smartor/specificArchiveGenerateConfig")
+public class PatSpecificArchiveGenerateConfigController extends BaseController {
+    @Autowired
+    private IPatSpecificArchiveGenerateConfigService patSpecificArchiveGenerateConfigService;
+
+    @ApiOperation("鏌ヨ涓撶妗f鐢熸垚閰嶇疆鍒楄〃")
+    //@PreAuthorize("@ss.hasPermi('system:specificArchiveGenerateConfig:list')")
+    @AddOrgId(field = "orgid", paramIndex = 0, campusField = "campusid")
+    @PostMapping("/selectPatSpecificArchiveGenerateConfigList")
+    public TableDataInfo selectPatSpecificArchiveGenerateConfigList(@RequestBody PatSpecificArchiveGenerateConfig patSpecificArchiveGenerateConfig) {
+        PageUtils.startPageByPost(patSpecificArchiveGenerateConfig.getPageNum(), patSpecificArchiveGenerateConfig.getPageSize());
+        List<PatSpecificArchiveGenerateConfig> list = patSpecificArchiveGenerateConfigService.selectPatSpecificArchiveGenerateConfigList(patSpecificArchiveGenerateConfig);
+        return getDataTable(list);
+    }
+
+    @ApiOperation("瀵煎嚭涓撶妗f鐢熸垚閰嶇疆鍒楄〃")
+    //@PreAuthorize("@ss.hasPermi('system:specificArchiveGenerateConfig:export')")
+    @Log(title = "涓撶妗f鐢熸垚閰嶇疆", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(HttpServletResponse response, PatSpecificArchiveGenerateConfig patSpecificArchiveGenerateConfig) {
+        List<PatSpecificArchiveGenerateConfig> list = patSpecificArchiveGenerateConfigService.selectPatSpecificArchiveGenerateConfigList(patSpecificArchiveGenerateConfig);
+        ExcelUtil<PatSpecificArchiveGenerateConfig> util = new ExcelUtil<PatSpecificArchiveGenerateConfig>(PatSpecificArchiveGenerateConfig.class);
+        util.exportExcel(response, list, "涓撶妗f鐢熸垚閰嶇疆鏁版嵁");
+    }
+
+    @ApiOperation("鑾峰彇涓撶妗f鐢熸垚閰嶇疆璇︾粏淇℃伅")
+    //@PreAuthorize("@ss.hasPermi('system:specificArchiveGenerateConfig:query')")
+    @GetMapping(value = "/getInfo/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id) {
+        return success(patSpecificArchiveGenerateConfigService.selectPatSpecificArchiveGenerateConfigById(id));
+    }
+
+    @ApiOperation("鏂板涓撶妗f鐢熸垚閰嶇疆")
+    //@PreAuthorize("@ss.hasPermi('system:specificArchiveGenerateConfig:add')")
+    @Log(title = "涓撶妗f鐢熸垚閰嶇疆", businessType = BusinessType.INSERT)
+    @AddOrgId(field = "orgid", paramIndex = 0, campusField = "campusid")
+    @PostMapping("/add")
+    public AjaxResult add(@RequestBody PatSpecificArchiveGenerateConfig patSpecificArchiveGenerateConfig) {
+        SysUser user = getLoginUser().getUser();
+        patSpecificArchiveGenerateConfig.setOrgid(user.getOrgid());
+        return toAjax(patSpecificArchiveGenerateConfigService.insertPatSpecificArchiveGenerateConfig(patSpecificArchiveGenerateConfig));
+    }
+
+    @ApiOperation("淇敼涓撶妗f鐢熸垚閰嶇疆")
+    //@PreAuthorize("@ss.hasPermi('system:specificArchiveGenerateConfig:edit')")
+    @Log(title = "涓撶妗f鐢熸垚閰嶇疆", businessType = BusinessType.UPDATE)
+    @AddOrgId(field = "orgid", paramIndex = 0, campusField = "campusid")
+    @PostMapping("/edit")
+    public AjaxResult edit(@RequestBody PatSpecificArchiveGenerateConfig patSpecificArchiveGenerateConfig) {
+        return toAjax(patSpecificArchiveGenerateConfigService.updatePatSpecificArchiveGenerateConfig(patSpecificArchiveGenerateConfig));
+    }
+
+    @ApiOperation("鍒犻櫎涓撶妗f鐢熸垚閰嶇疆")
+    //@PreAuthorize("@ss.hasPermi('system:specificArchiveGenerateConfig:remove')")
+    @Log(title = "涓撶妗f鐢熸垚閰嶇疆", businessType = BusinessType.DELETE)
+    @GetMapping("/remove/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids) {
+        return toAjax(patSpecificArchiveGenerateConfigService.deletePatSpecificArchiveGenerateConfigByIds(ids));
+    }
+
+    @ApiOperation("鎵归噺鍒犻櫎涓撶妗f鐢熸垚閰嶇疆")
+    //@PreAuthorize("@ss.hasPermi('system:specificArchiveGenerateConfig:remove')")
+    @Log(title = "涓撶妗f鐢熸垚閰嶇疆", businessType = BusinessType.DELETE)
+    @PostMapping("/batchDelete")
+    public AjaxResult batchDelete(@RequestBody Long[] ids) {
+        return toAjax(patSpecificArchiveGenerateConfigService.batchDeletePatSpecificArchiveGenerateConfigByIds(ids));
+    }
+
+    /**
+     * 鏂板鎴栦慨鏀逛笓绉戞。妗堢敓鎴愰厤缃�
+     */
+    @ApiOperation("鏂板鎴栦慨鏀逛笓绉戞。妗堢敓鎴愰厤缃�")
+    @PostMapping("/insertOrUpdatePatSpecificArchiveGenerateConfig")
+    public AjaxResult insertOrUpdatePatSpecificArchiveGenerateConfig(@RequestBody PatSpecificArchiveGenerateConfig patSpecificArchiveGenerateConfig) {
+        LoginUser loginUser = getLoginUser();
+        SysUser user = loginUser.getUser();
+        patSpecificArchiveGenerateConfig.setCreateBy(user.getNickName());
+        patSpecificArchiveGenerateConfig.setUpdateBy(user.getNickName());
+        patSpecificArchiveGenerateConfig.setOrgid(user.getOrgid());
+        if (StringUtils.isNotEmpty(user.getCampusid())) {
+            patSpecificArchiveGenerateConfig.setCampusid(user.getCampusid());
+        } else {
+            patSpecificArchiveGenerateConfig.setCampusid("1");
+        }
+        return toAjax(patSpecificArchiveGenerateConfigService.insertOrUpdatePatSpecificArchiveGenerateConfig(patSpecificArchiveGenerateConfig));
+    }
+}
diff --git a/ruoyi-patarchive/src/main/java/com/ruoyi/patarchive/domain/PatArchiveReq.java b/ruoyi-patarchive/src/main/java/com/ruoyi/patarchive/domain/PatArchiveReq.java
index eb4f7c1..09c309f 100644
--- a/ruoyi-patarchive/src/main/java/com/ruoyi/patarchive/domain/PatArchiveReq.java
+++ b/ruoyi-patarchive/src/main/java/com/ruoyi/patarchive/domain/PatArchiveReq.java
@@ -227,6 +227,18 @@
     private Integer cry;
 
     /**
+     * 鍒涘缓鏃堕棿
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private Date createTimeStart;
+
+    /**
+     * 鍒涘缓鏃堕棿
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private Date createTimeEnd;
+
+    /**
      * 鏈烘瀯ID
      */
     @ApiModelProperty("闄㈠尯")
diff --git a/ruoyi-patarchive/src/main/java/com/ruoyi/patarchive/domain/PatSpecificArchiveGenerateConfig.java b/ruoyi-patarchive/src/main/java/com/ruoyi/patarchive/domain/PatSpecificArchiveGenerateConfig.java
new file mode 100644
index 0000000..d6c345f
--- /dev/null
+++ b/ruoyi-patarchive/src/main/java/com/ruoyi/patarchive/domain/PatSpecificArchiveGenerateConfig.java
@@ -0,0 +1,60 @@
+package com.ruoyi.patarchive.domain;
+
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.core.domain.BaseEntity;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 涓撶妗f鐢熸垚閰嶇疆瀵硅薄 pat_specific_archive_generate_config
+ *
+ * @author ruoyi
+ * @date 2026-10-12
+ */
+@Data
+@ApiModel(value = "涓撶妗f鐢熸垚閰嶇疆瀵硅薄")
+public class PatSpecificArchiveGenerateConfig extends BaseEntity {
+    private static final long serialVersionUID = 1L;
+
+    /** 閰嶇疆琛ㄤ富閿甀D */
+    @ApiModelProperty(value = "閰嶇疆琛ㄤ富閿甀D")
+    private Long id;
+
+    /** 閮ㄩ棬绫诲瀷锛�1绉戝锛�2鐥呭尯 */
+    @Excel(name = "閮ㄩ棬绫诲瀷")
+    @ApiModelProperty(value = "閮ㄩ棬绫诲瀷锛�1绉戝锛�2鐥呭尯")
+    private String deptType;
+
+    /** 閮ㄩ棬鍒嗙被: 1濡囩 2鍎跨 */
+    @Excel(name = "閮ㄩ棬鍒嗙被")
+    @ApiModelProperty(value = "閮ㄩ棬鍒嗙被: 1濡囩 2鍎跨")
+    private String deptCategory;
+
+    /** 妗f绫诲瀷: 濡囩妗f绫诲瀷(1 鏃╁瓡鍏崇埍2 鍥寸粷缁忔湡MHI),鍎跨妗f绫诲瀷(1钀ュ吇鎬х柧鐥� 2瀛︿範鍥伴毦 3楂樺嵄鍎夸笓妗�) */
+    @Excel(name = "妗f绫诲瀷")
+    @ApiModelProperty(value = "妗f绫诲瀷: 濡囩妗f绫诲瀷(1 鏃╁瓡鍏崇埍2 鍥寸粷缁忔湡MHI),鍎跨妗f绫诲瀷(1钀ュ吇鎬х柧鐥� 2瀛︿範鍥伴毦 3楂樺嵄鍎夸笓妗�)")
+    private String archiveType;
+
+    /** 绉戝Code闆嗗悎(JSON鏍煎紡锛屽 ["D001","D002"]) */
+    @ApiModelProperty(value = "绉戝Code闆嗗悎(JSON鏍煎紡锛屽 [\"D001\",\"D002\"])")
+    private String deptCodes;
+
+    /** 璇婃柇code闆嗗悎(JSON鏍煎紡锛屽 ["E10.901","E11.901"]) */
+    @ApiModelProperty(value = "璇婃柇code闆嗗悎(JSON鏍煎紡锛屽 [\"E10.901\",\"E11.901\"])")
+    private String icd10Codes;
+
+    /** 鍖婚櫌鏈烘瀯ID */
+    @Excel(name = "鍖婚櫌鏈烘瀯ID")
+    @ApiModelProperty(value = "鍖婚櫌鏈烘瀯ID")
+    private String orgid;
+
+    /** 闄㈠尯ID */
+    @Excel(name = "闄㈠尯ID")
+    @ApiModelProperty(value = "闄㈠尯ID")
+    private String campusid;
+
+    /** 鍒犻櫎鏍囧織锛�0锛氭湭鍒犻櫎锛�1锛氬凡鍒犻櫎锛� */
+    @ApiModelProperty(value = "鍒犻櫎鏍囧織锛�0锛氭湭鍒犻櫎锛�1锛氬凡鍒犻櫎锛�")
+    private String delFlag;
+}
diff --git a/ruoyi-patarchive/src/main/java/com/ruoyi/patarchive/mapper/PatArchiveMapper.java b/ruoyi-patarchive/src/main/java/com/ruoyi/patarchive/mapper/PatArchiveMapper.java
index 7f6d911..09c41d6 100644
--- a/ruoyi-patarchive/src/main/java/com/ruoyi/patarchive/mapper/PatArchiveMapper.java
+++ b/ruoyi-patarchive/src/main/java/com/ruoyi/patarchive/mapper/PatArchiveMapper.java
@@ -24,6 +24,14 @@
      */
     public PatArchive selectPatArchiveByPatid(Long patid);
 
+    /**
+     * 閿佸畾鎮h�呮。妗堣锛屼覆琛屽寲鍚屼竴鎮h�呯殑浜哄伐闅忚鍒涘缓
+     *
+     * @param patid 鎮h�呮。妗堜富閿�
+     * @return 鎮h�呮。妗堜富閿紝妗f涓嶅瓨鍦ㄦ垨宸插垹闄ゆ椂杩斿洖 null
+     */
+    Long lockPatArchiveByPatid(@Param("patid") Long patid);
+
     List<PatArchive> selectPatArchiveByPatnos(@Param("patnos") List<String> patnos);
 
     List<PatArchive> selectPatArchiveByPatidHiss(@Param("patidHiss") List<String> patidHiss);
diff --git a/ruoyi-patarchive/src/main/java/com/ruoyi/patarchive/mapper/PatSpecificArchiveGenerateConfigMapper.java b/ruoyi-patarchive/src/main/java/com/ruoyi/patarchive/mapper/PatSpecificArchiveGenerateConfigMapper.java
new file mode 100644
index 0000000..4ced3cb
--- /dev/null
+++ b/ruoyi-patarchive/src/main/java/com/ruoyi/patarchive/mapper/PatSpecificArchiveGenerateConfigMapper.java
@@ -0,0 +1,28 @@
+package com.ruoyi.patarchive.mapper;
+
+import com.ruoyi.patarchive.domain.PatSpecificArchiveGenerateConfig;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 涓撶妗f鐢熸垚閰嶇疆Mapper鎺ュ彛
+ *
+ * @author ruoyi
+ * @date 2026-10-12
+ */
+@Mapper
+public interface PatSpecificArchiveGenerateConfigMapper
+{
+    public PatSpecificArchiveGenerateConfig selectPatSpecificArchiveGenerateConfigById(Long id);
+
+    public List<PatSpecificArchiveGenerateConfig> selectPatSpecificArchiveGenerateConfigList(PatSpecificArchiveGenerateConfig patSpecificArchiveGenerateConfig);
+
+    public int insertPatSpecificArchiveGenerateConfig(PatSpecificArchiveGenerateConfig patSpecificArchiveGenerateConfig);
+
+    public int updatePatSpecificArchiveGenerateConfig(PatSpecificArchiveGenerateConfig patSpecificArchiveGenerateConfig);
+
+    public int deletePatSpecificArchiveGenerateConfigById(Long id);
+
+    public int deletePatSpecificArchiveGenerateConfigByIds(Long[] ids);
+}
diff --git a/ruoyi-patarchive/src/main/java/com/ruoyi/patarchive/service/IPatSpecificArchiveGenerateConfigService.java b/ruoyi-patarchive/src/main/java/com/ruoyi/patarchive/service/IPatSpecificArchiveGenerateConfigService.java
new file mode 100644
index 0000000..3d3bb94
--- /dev/null
+++ b/ruoyi-patarchive/src/main/java/com/ruoyi/patarchive/service/IPatSpecificArchiveGenerateConfigService.java
@@ -0,0 +1,30 @@
+package com.ruoyi.patarchive.service;
+
+import com.ruoyi.patarchive.domain.PatSpecificArchiveGenerateConfig;
+
+import java.util.List;
+
+/**
+ * 涓撶妗f鐢熸垚閰嶇疆Service鎺ュ彛
+ *
+ * @author ruoyi
+ * @date 2026-10-12
+ */
+public interface IPatSpecificArchiveGenerateConfigService
+{
+    public PatSpecificArchiveGenerateConfig selectPatSpecificArchiveGenerateConfigById(Long id);
+
+    public List<PatSpecificArchiveGenerateConfig> selectPatSpecificArchiveGenerateConfigList(PatSpecificArchiveGenerateConfig patSpecificArchiveGenerateConfig);
+
+    public int insertPatSpecificArchiveGenerateConfig(PatSpecificArchiveGenerateConfig patSpecificArchiveGenerateConfig);
+
+    public int updatePatSpecificArchiveGenerateConfig(PatSpecificArchiveGenerateConfig patSpecificArchiveGenerateConfig);
+
+    public int deletePatSpecificArchiveGenerateConfigByIds(Long[] ids);
+
+    public int batchDeletePatSpecificArchiveGenerateConfigByIds(Long[] ids);
+
+    public int deletePatSpecificArchiveGenerateConfigById(Long id);
+
+    public int insertOrUpdatePatSpecificArchiveGenerateConfig(PatSpecificArchiveGenerateConfig patSpecificArchiveGenerateConfig);
+}
diff --git a/ruoyi-patarchive/src/main/java/com/ruoyi/patarchive/service/impl/PatSpecificArchiveGenerateConfigServiceImpl.java b/ruoyi-patarchive/src/main/java/com/ruoyi/patarchive/service/impl/PatSpecificArchiveGenerateConfigServiceImpl.java
new file mode 100644
index 0000000..1394d8e
--- /dev/null
+++ b/ruoyi-patarchive/src/main/java/com/ruoyi/patarchive/service/impl/PatSpecificArchiveGenerateConfigServiceImpl.java
@@ -0,0 +1,88 @@
+package com.ruoyi.patarchive.service.impl;
+
+import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.patarchive.domain.PatSpecificArchiveGenerateConfig;
+import com.ruoyi.patarchive.mapper.PatSpecificArchiveGenerateConfigMapper;
+import com.ruoyi.patarchive.service.IPatSpecificArchiveGenerateConfigService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 涓撶妗f鐢熸垚閰嶇疆Service涓氬姟灞傚鐞�
+ *
+ * @author ruoyi
+ * @date 2026-10-12
+ */
+@Service
+public class PatSpecificArchiveGenerateConfigServiceImpl implements IPatSpecificArchiveGenerateConfigService
+{
+    @Autowired
+    private PatSpecificArchiveGenerateConfigMapper patSpecificArchiveGenerateConfigMapper;
+
+    @Override
+    public PatSpecificArchiveGenerateConfig selectPatSpecificArchiveGenerateConfigById(Long id)
+    {
+        return patSpecificArchiveGenerateConfigMapper.selectPatSpecificArchiveGenerateConfigById(id);
+    }
+
+    @Override
+    public List<PatSpecificArchiveGenerateConfig> selectPatSpecificArchiveGenerateConfigList(PatSpecificArchiveGenerateConfig patSpecificArchiveGenerateConfig)
+    {
+        return patSpecificArchiveGenerateConfigMapper.selectPatSpecificArchiveGenerateConfigList(patSpecificArchiveGenerateConfig);
+    }
+
+    @Override
+    public int insertPatSpecificArchiveGenerateConfig(PatSpecificArchiveGenerateConfig patSpecificArchiveGenerateConfig)
+    {
+        patSpecificArchiveGenerateConfig.setCreateTime(DateUtils.getNowDate());
+        return patSpecificArchiveGenerateConfigMapper.insertPatSpecificArchiveGenerateConfig(patSpecificArchiveGenerateConfig);
+    }
+
+    @Override
+    public int updatePatSpecificArchiveGenerateConfig(PatSpecificArchiveGenerateConfig patSpecificArchiveGenerateConfig)
+    {
+        patSpecificArchiveGenerateConfig.setUpdateTime(DateUtils.getNowDate());
+        return patSpecificArchiveGenerateConfigMapper.updatePatSpecificArchiveGenerateConfig(patSpecificArchiveGenerateConfig);
+    }
+
+    @Override
+    public int insertOrUpdatePatSpecificArchiveGenerateConfig(PatSpecificArchiveGenerateConfig patSpecificArchiveGenerateConfig)
+    {
+        if (patSpecificArchiveGenerateConfig.getId() != null
+                && patSpecificArchiveGenerateConfigMapper.selectPatSpecificArchiveGenerateConfigById(patSpecificArchiveGenerateConfig.getId()) != null)
+        {
+            patSpecificArchiveGenerateConfig.setUpdateTime(DateUtils.getNowDate());
+            return patSpecificArchiveGenerateConfigMapper.updatePatSpecificArchiveGenerateConfig(patSpecificArchiveGenerateConfig);
+        }
+        else
+        {
+            patSpecificArchiveGenerateConfig.setCreateTime(DateUtils.getNowDate());
+            if (StringUtils.isEmpty(patSpecificArchiveGenerateConfig.getDelFlag()))
+            {
+                patSpecificArchiveGenerateConfig.setDelFlag("0");
+            }
+            return patSpecificArchiveGenerateConfigMapper.insertPatSpecificArchiveGenerateConfig(patSpecificArchiveGenerateConfig);
+        }
+    }
+
+    @Override
+    public int batchDeletePatSpecificArchiveGenerateConfigByIds(Long[] ids)
+    {
+        return patSpecificArchiveGenerateConfigMapper.deletePatSpecificArchiveGenerateConfigByIds(ids);
+    }
+
+    @Override
+    public int deletePatSpecificArchiveGenerateConfigByIds(Long[] ids)
+    {
+        return patSpecificArchiveGenerateConfigMapper.deletePatSpecificArchiveGenerateConfigByIds(ids);
+    }
+
+    @Override
+    public int deletePatSpecificArchiveGenerateConfigById(Long id)
+    {
+        return patSpecificArchiveGenerateConfigMapper.deletePatSpecificArchiveGenerateConfigById(id);
+    }
+}
diff --git a/ruoyi-patarchive/src/main/resources/mapper/patarchive/PatArchiveMapper.xml b/ruoyi-patarchive/src/main/resources/mapper/patarchive/PatArchiveMapper.xml
index 2f038f0..adbce7a 100644
--- a/ruoyi-patarchive/src/main/resources/mapper/patarchive/PatArchiveMapper.xml
+++ b/ruoyi-patarchive/src/main/resources/mapper/patarchive/PatArchiveMapper.xml
@@ -214,6 +214,12 @@
             <if test="casePersonAge != null ">and case_person_age = #{casePersonAge}</if>
             <if test="filterDrname != null ">and filter_drname = #{filterDrname}</if>
             <if test="filterDrcode != null ">and filter_drcode = #{filterDrcode}</if>
+            <if test="createTimeStart != null">
+                AND date_format(create_time,'%y%m%d') &gt;= date_format(#{createTimeStart},'%y%m%d')
+            </if>
+            <if test="createTimeEnd != null">
+                AND date_format(create_time,'%y%m%d') &lt;= date_format(#{createTimeEnd},'%y%m%d ')
+            </if>
         <if test="pageSize != null  and pageNum != null">limit ${pageSize} OFFSET ${pageNum}</if>
     </select>
 
@@ -262,6 +268,12 @@
             <if test="idcardno != null  and idcardno != ''">and idcardno = #{idcardno}</if>
             <if test="telcode != null  and telcode != ''">and telcode = #{telcode}</if>
             <if test="campusid != null  and campusid != ''">and campusid = #{campusid}</if>
+            <if test="createTimeStart != null">
+                AND date_format(a.create_time,'%y%m%d') &gt;= date_format(#{createTimeStart},'%y%m%d')
+            </if>
+            <if test="createTimeEnd != null">
+                AND date_format(a.create_time,'%y%m%d') &lt;= date_format(#{createTimeEnd},'%y%m%d ')
+            </if>
             <if test="tagIds != null  and tagIds != ''">and c.tagid in
                 <foreach collection="tagIds" item="tagId" open="(" separator="," close=")">
                     #{tagId}
@@ -272,6 +284,14 @@
     <select id="selectPatArchiveByPatid" parameterType="Long" resultMap="PatArchiveResult">
         <include refid="selectPatArchiveVo"/>
         where id = #{id} and del_flag=0
+    </select>
+
+    <!-- 涓茶鍖栧悓涓�鎮h�呯殑浜哄伐闅忚鍒涘缓锛岄伩鍏嶅苟鍙戦噸澶嶆牎楠屽け鏁� -->
+    <select id="lockPatArchiveByPatid" resultType="java.lang.Long">
+        select id
+        from pat_archive
+        where id = #{patid} and del_flag = 0
+        for update
     </select>
 
     <select id="selectPatArchiveByPatnos" resultMap="PatArchiveResult">
@@ -543,6 +563,12 @@
                     #{tagId}
                 </foreach>
             </if>
+            <if test="createTimeStart != null">
+                AND date_format(a.create_time,'%y%m%d') &gt;= date_format(#{createTimeStart},'%y%m%d')
+            </if>
+            <if test="createTimeEnd != null">
+                AND date_format(a.create_time,'%y%m%d') &lt;= date_format(#{createTimeEnd},'%y%m%d ')
+            </if>
         order by a.update_time desc
         <if test="pn != null  and ps != null">limit ${pn},${ps}</if>
 
@@ -777,6 +803,12 @@
                     #{pid}
                 </foreach>
             </if>
+            <if test="createTimeStart != null">
+                AND date_format(a.create_time,'%y%m%d') &gt;= date_format(#{createTimeStart},'%y%m%d')
+            </if>
+            <if test="createTimeEnd != null">
+                AND date_format(a.create_time,'%y%m%d') &lt;= date_format(#{createTimeEnd},'%y%m%d ')
+            </if>
             <if test="isFilter == true">
                 AND NOT exists ( SELECT 1 FROM pat_filterlist f where d.patid=f.patid)
             </if>
@@ -868,6 +900,12 @@
                     #{pid}
                 </foreach>
             </if>
+            <if test="createTimeStart != null">
+                AND date_format(a.create_time,'%y%m%d') &gt;= date_format(#{createTimeStart},'%y%m%d')
+            </if>
+            <if test="createTimeEnd != null">
+                AND date_format(a.create_time,'%y%m%d') &lt;= date_format(#{createTimeEnd},'%y%m%d ')
+            </if>
             <if test="isFilter == true">
                 AND NOT exists ( SELECT 1 FROM pat_filterlist f where d.patid=f.patid)
             </if>
@@ -945,6 +983,12 @@
             <if test="pid != null  and pid != ''">and a.id = #{pid}</if>
             <if test="telcode != null  and telcode != ''">and a.telcode = #{telcode}</if>
             <if test="notrequiredFlag != null  and notrequiredFlag != ''">and a.notrequired_flag = #{notrequiredFlag}
+            </if>
+            <if test="createTimeStart != null">
+                AND date_format(a.create_time,'%y%m%d') &gt;= date_format(#{createTimeStart},'%y%m%d')
+            </if>
+            <if test="createTimeEnd != null">
+                AND date_format(a.create_time,'%y%m%d') &lt;= date_format(#{createTimeEnd},'%y%m%d ')
             </if>
             <if test="hospitalname != null and hospitalname != ''">
                 AND d.hospitalname = #{hospitalname}
@@ -1195,6 +1239,12 @@
                     #{pid}
                 </foreach>
             </if>
+            <if test="createTimeStart != null">
+                AND date_format(a.create_time,'%y%m%d') &gt;= date_format(#{createTimeStart},'%y%m%d')
+            </if>
+            <if test="createTimeEnd != null">
+                AND date_format(a.create_time,'%y%m%d') &lt;= date_format(#{createTimeEnd},'%y%m%d ')
+            </if>
             <if test="isFilter == true">
                 AND NOT exists ( SELECT 1 FROM pat_filterlist f where d.patid=f.patid)
             </if>
diff --git a/ruoyi-patarchive/src/main/resources/mapper/patarchive/PatSpecificArchiveGenerateConfigMapper.xml b/ruoyi-patarchive/src/main/resources/mapper/patarchive/PatSpecificArchiveGenerateConfigMapper.xml
new file mode 100644
index 0000000..ceacd4f
--- /dev/null
+++ b/ruoyi-patarchive/src/main/resources/mapper/patarchive/PatSpecificArchiveGenerateConfigMapper.xml
@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.patarchive.mapper.PatSpecificArchiveGenerateConfigMapper">
+
+    <resultMap type="com.ruoyi.patarchive.domain.PatSpecificArchiveGenerateConfig" id="PatSpecificArchiveGenerateConfigResult">
+        <result property="id" column="id"/>
+        <result property="deptType" column="dept_type"/>
+        <result property="deptCategory" column="dept_category"/>
+        <result property="archiveType" column="archive_type"/>
+        <result property="deptCodes" column="dept_codes"/>
+        <result property="icd10Codes" column="icd10_codes"/>
+        <result property="remark" column="remark"/>
+        <result property="orgid" column="orgid"/>
+        <result property="campusid" column="campusid"/>
+        <result property="updateBy" column="update_by"/>
+        <result property="updateTime" column="update_time"/>
+        <result property="createBy" column="create_by"/>
+        <result property="createTime" column="create_time"/>
+        <result property="delFlag" column="del_flag"/>
+    </resultMap>
+
+    <sql id="selectPatSpecificArchiveGenerateConfigVo">
+        select id,
+               dept_type,
+               dept_category,
+               archive_type,
+               dept_codes,
+               icd10_codes,
+               remark,
+               orgid,
+               campusid,
+               update_by,
+               update_time,
+               create_by,
+               create_time,
+               del_flag
+        from pat_specific_archive_generate_config
+    </sql>
+
+    <select id="selectPatSpecificArchiveGenerateConfigList" parameterType="com.ruoyi.patarchive.domain.PatSpecificArchiveGenerateConfig"
+            resultMap="PatSpecificArchiveGenerateConfigResult">
+        <include refid="selectPatSpecificArchiveGenerateConfigVo"/>
+        where 1=1
+            and del_flag = '0'
+            <if test="deptType != null and deptType != ''">and dept_type = #{deptType}</if>
+            <if test="deptCategory != null and deptCategory != ''">and dept_category = #{deptCategory}</if>
+            <if test="archiveType != null and archiveType != ''">and archive_type = #{archiveType}</if>
+            <if test="orgid != null and orgid != ''">and orgid = #{orgid}</if>
+            <if test="campusid != null and campusid != ''">and campusid = #{campusid}</if>
+            <if test="params.beginTime != null and params.beginTime != ''">
+                and date_format(create_time, '%y%m%d') &gt;= date_format(#{params.beginTime}, '%y%m%d')
+            </if>
+            <if test="params.endTime != null and params.endTime != ''">
+                and date_format(create_time, '%y%m%d') &lt;= date_format(#{params.endTime}, '%y%m%d')
+            </if>
+    </select>
+
+    <select id="selectPatSpecificArchiveGenerateConfigById" parameterType="Long" resultMap="PatSpecificArchiveGenerateConfigResult">
+        <include refid="selectPatSpecificArchiveGenerateConfigVo"/>
+        where id = #{id}
+    </select>
+
+    <insert id="insertPatSpecificArchiveGenerateConfig" parameterType="com.ruoyi.patarchive.domain.PatSpecificArchiveGenerateConfig"
+            useGeneratedKeys="true" keyProperty="id">
+        insert into pat_specific_archive_generate_config
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="deptType != null">dept_type,</if>
+            <if test="deptCategory != null">dept_category,</if>
+            <if test="archiveType != null">archive_type,</if>
+            <if test="deptCodes != null">dept_codes,</if>
+            <if test="icd10Codes != null">icd10_codes,</if>
+            <if test="remark != null">remark,</if>
+            <if test="orgid != null">orgid,</if>
+            <if test="campusid != null">campusid,</if>
+            <if test="updateBy != null">update_by,</if>
+            <if test="updateTime != null">update_time,</if>
+            <if test="createBy != null">create_by,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="delFlag != null and delFlag != ''">del_flag,</if>
+        </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="deptType != null">#{deptType},</if>
+            <if test="deptCategory != null">#{deptCategory},</if>
+            <if test="archiveType != null">#{archiveType},</if>
+            <if test="deptCodes != null">#{deptCodes},</if>
+            <if test="icd10Codes != null">#{icd10Codes},</if>
+            <if test="remark != null">#{remark},</if>
+            <if test="orgid != null">#{orgid},</if>
+            <if test="campusid != null">#{campusid},</if>
+            <if test="updateBy != null">#{updateBy},</if>
+            <if test="updateTime != null">#{updateTime},</if>
+            <if test="createBy != null">#{createBy},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="delFlag != null and delFlag != ''">#{delFlag},</if>
+        </trim>
+    </insert>
+
+    <update id="updatePatSpecificArchiveGenerateConfig" parameterType="com.ruoyi.patarchive.domain.PatSpecificArchiveGenerateConfig">
+        update pat_specific_archive_generate_config
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="deptType != null">dept_type = #{deptType},</if>
+            <if test="deptCategory != null">dept_category = #{deptCategory},</if>
+            <if test="archiveType != null">archive_type = #{archiveType},</if>
+            <if test="deptCodes != null">dept_codes = #{deptCodes},</if>
+            <if test="icd10Codes != null">icd10_codes = #{icd10Codes},</if>
+            <if test="remark != null">remark = #{remark},</if>
+            <if test="orgid != null">orgid = #{orgid},</if>
+            <if test="campusid != null">campusid = #{campusid},</if>
+            <if test="updateBy != null">update_by = #{updateBy},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+            <if test="createBy != null">create_by = #{createBy},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="delFlag != null and delFlag != ''">del_flag = #{delFlag},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <update id="deletePatSpecificArchiveGenerateConfigById" parameterType="Long">
+        update pat_specific_archive_generate_config
+        <trim prefix="SET" suffixOverrides=",">
+            del_flag = '1'
+        </trim>
+        where id = #{id}
+    </update>
+
+    <update id="deletePatSpecificArchiveGenerateConfigByIds" parameterType="String">
+        update pat_specific_archive_generate_config set del_flag = '1' where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </update>
+</mapper>
diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/CollectHISController.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/CollectHISController.java
index 89f413e..67b4806 100644
--- a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/CollectHISController.java
+++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/CollectHISController.java
@@ -51,13 +51,16 @@
     }
 
     /**
-     * 鐢ㄦ埛淇℃伅闆嗗悎淇℃伅鏁版嵁閲囬泦
+     * 鐢ㄦ埛淇℃伅闆嗗悎淇℃伅鏁版嵁閲囬泦锛堝紓姝ユ墽琛岋紝鎺ュ彛绔嬪嵆杩斿洖锛�
      */
     @PostMapping("/collectUser")
     @ApiOperation("鍚屾鐢ㄦ埛淇℃伅")
     public AjaxResult selectUserList(@RequestBody SysUser sysUser) {
-        Integer integer = ichService.selectUserList(sysUser);
-        return AjaxResult.success(integer);
+        boolean submitted = ichService.selectUserListAsync(sysUser);
+        if (!submitted) {
+            return AjaxResult.warn("鐢ㄦ埛淇℃伅鍚屾浠诲姟姝e湪鎵ц涓紝璇峰嬁閲嶅鎻愪氦");
+        }
+        return AjaxResult.success("鐢ㄦ埛淇℃伅鍚屾浠诲姟宸叉彁浜わ紝鍚庡彴鎵ц涓紝璇烽�氳繃鏃ュ織鏌ョ湅杩涘害");
     }
 
     /**
diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ICollectHISService.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ICollectHISService.java
index 9de9211..e2892fe 100644
--- a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ICollectHISService.java
+++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ICollectHISService.java
@@ -71,6 +71,14 @@
     public Integer selectUserList(SysUser sysUser);
 
     /**
+     * 寮傛鍚屾鐢ㄦ埛淇℃伅锛堟彁浜ゅ悗绔嬪嵆杩斿洖锛岀敱鍚庡彴绾跨▼鎵ц锛�
+     *
+     * @param sysUser 鐢ㄦ埛淇℃伅
+     * @return true=宸叉彁浜わ紱false=宸叉湁鍚屾浠诲姟鍦ㄦ墽琛�
+     */
+    public boolean selectUserListAsync(SysUser sysUser);
+
+    /**
      * 鏌ヨ閮ㄩ棬绠$悊鏁版嵁
      *
      * @param dept 閮ㄩ棬淇℃伅
diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/CollectHISServiceImpl.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/CollectHISServiceImpl.java
index df63314..29f9a5a 100644
--- a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/CollectHISServiceImpl.java
+++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/CollectHISServiceImpl.java
@@ -27,6 +27,7 @@
 import java.time.LocalDateTime;
 import java.time.ZoneId;
 import java.util.*;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -100,6 +101,11 @@
 
     @Autowired
     private RedisCache redisCache;
+
+    /**
+     * 鐢ㄦ埛鍚屾浠诲姟杩愯鏍囪锛岄伩鍏嶉噸澶嶇偣鍑�/閲嶅璋冪敤骞跺彂璺戝叏閲忓悓姝�
+     */
+    private final AtomicBoolean userSyncRunning = new AtomicBoolean(false);
 
     private static final long SHARDING_THRESHOLD = 5_000_000L;
     private static final String PAT_ARCHIVE_TABLE = "pat_archive";
@@ -723,6 +729,39 @@
         return size;
     }
 
+    /**
+     * 寮傛鍚屾鐢ㄦ埛淇℃伅锛氭帴鍙g珛鍗宠繑鍥烇紝瀹為檯鍚屾浜ょ粰绾跨▼姹犲悗鍙版墽琛屻��
+     * 浣跨敤 AtomicBoolean 闃叉閲嶅鎻愪氦瀵艰嚧澶氫釜鍏ㄩ噺鍚屾浠诲姟骞跺彂鎵ц銆�
+     */
+    @Override
+    public boolean selectUserListAsync(SysUser sysUser) {
+        if (!userSyncRunning.compareAndSet(false, true)) {
+            log.warn("鐢ㄦ埛淇℃伅鍚屾浠诲姟姝e湪鎵ц涓紝蹇界暐鏈鎻愪氦");
+            return false;
+        }
+        try {
+            taskExecutor.execute(() -> {
+                long start = System.currentTimeMillis();
+                log.info("鐢ㄦ埛淇℃伅寮傛鍚屾浠诲姟寮�濮嬫墽琛�");
+                try {
+                    Integer count = selectUserList(sysUser);
+                    log.info("鐢ㄦ埛淇℃伅寮傛鍚屾浠诲姟鎵ц瀹屾垚锛屽鐞嗘暟閲忥細{}锛岃�楁椂锛歿}ms",
+                            count, System.currentTimeMillis() - start);
+                } catch (Throwable e) {
+                    log.error("鐢ㄦ埛淇℃伅寮傛鍚屾浠诲姟鎵ц澶辫触锛岃�楁椂锛歿}ms",
+                            System.currentTimeMillis() - start, e);
+                } finally {
+                    userSyncRunning.set(false);
+                }
+            });
+            return true;
+        } catch (RuntimeException e) {
+            userSyncRunning.set(false);
+            log.error("鐢ㄦ埛淇℃伅寮傛鍚屾浠诲姟鎻愪氦澶辫触", e);
+            return false;
+        }
+    }
+
     @Override
     public Integer selectUserList(SysUser sysUser) {
         List<SysUser> sysUserList = chMapper.selectUserList(sysUser);
@@ -837,7 +876,12 @@
         log.info("selectDeptList鐨勯噰闆嗗埌鐨勬暟閲忎负锛歿}", sysDepts.size());
         // count = sysDept2Mapper.batchDept(sysDepts);
         for (SysDept row : sysDepts) {
-            SysDept sysDeptTemp = sysDept2Mapper.selectDeptByCode(row.getDeptCode(), null);
+            // 绉戝鍜岀梾鍖哄彲鑳藉叡鐢ㄥ悓涓�涓� dept_code锛屽繀椤绘寜 dept_type + orgid 绮剧‘瀹氫綅锛屽惁鍒� selectOne 浼氳繑鍥炲鏉�
+            if (StringUtils.isEmpty(row.getDeptCode())) {
+                log.warn("selectDeptList璺宠繃deptCode涓虹┖鐨勮褰曪細{}", row);
+                continue;
+            }
+            SysDept sysDeptTemp = sysDept2Mapper.selectDeptByCodeAndType(row.getDeptCode(), row.getOrgid(), row.getDeptType());
             if (sysDeptTemp != null && sysDeptTemp.getDeptName() != null) {
                 row.setDeptId(sysDeptTemp.getDeptId());
                 count += sysDept2Mapper.updateDept(row);
diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java
index 5340943..3f3c482 100644
--- a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java
+++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java
@@ -14,6 +14,7 @@
 import com.ruoyi.common.enums.ServiceFromEnum;
 import com.ruoyi.common.utils.*;
 import com.ruoyi.common.utils.http.HttpUtils;
+import com.ruoyi.common.utils.sms.SmsUtils;
 import com.ruoyi.message.SendResult;
 import com.ruoyi.message.sms.SmsSendRequest;
 import com.ruoyi.message.sms.SmsSenderFactory;
@@ -39,6 +40,7 @@
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.math.NumberUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Component;
@@ -1149,6 +1151,13 @@
             serviceSubtaskList.addAll(compensateServiceSubtaskList);
         }
         for (ServiceSubtask serviceSubtask : serviceSubtaskList) {
+            //currentPreachform涓虹┖锛岃鏄庤瀛愪换鍔¤繕娌℃湁鎵ц杩囦换浣曚竴绉嶅彂閫佹柟寮�(sendstate=1锛屽睘浜庨娆¤ˉ鍋�)锛�
+            //杩欓噷鐩存帴浜ょ粰sfHandlle澶勭悊锛屽畠浼氭寜sort=1鐨勭涓�绉嶅彂閫佹柟寮忓彂閫侊紝涓巊etSendPreachform涓殑澶勭悊淇濇寔涓�鑷�
+            if (StringUtils.isEmpty(serviceSubtask.getCurrentPreachform())) {
+                log.warn("銆恈ompensateTask銆憇ubid锛歿}锛宑urrentPreachform涓虹┖锛屾寜绗竴绉嶅彂閫佹柟寮忚ˉ鍋垮鐞�", serviceSubtask.getId());
+                sfHandlle(serviceSubtask);
+                continue;
+            }
             //鏍规嵁褰撳墠鐨勬墽琛屾柟寮忥紝鑾峰彇涓嬩竴绉嶆墽琛屾柟寮�
             ServiceSubtaskPreachform serviceSubtaskPreachform = new ServiceSubtaskPreachform();
             serviceSubtaskPreachform.setTaskid(serviceSubtask.getTaskid());
@@ -1217,6 +1226,24 @@
         LocalDate today = LocalDate.now();
         // 濡傛灉鏃ユ湡鏃╀簬鎴栫瓑浜庝粖澶╋紝杩斿洖 true
         return !localDate.isAfter(today);
+    }
+
+    /**
+     * 鎸夋満鏋勬煡璇㈤厤缃�硷紙鍚屼竴 key 鍦ㄤ笉鍚屾満鏋勪笅鍙厤缃笉鍚屽�硷級
+     *
+     * @param configKey 閰嶇疆key
+     * @param orgid     鏈烘瀯ID
+     * @return 閰嶇疆鍊硷紝鏈厤缃繑鍥� null
+     */
+    private String getConfigValue(String configKey, String orgid) {
+        SysConfig query = new SysConfig();
+        query.setConfigKey(configKey);
+        query.setOrgid(orgid);
+        List<SysConfig> sysConfigs = configService.selectConfigList(query);
+        if (CollectionUtils.isNotEmpty(sysConfigs)) {
+            return sysConfigs.get(0).getConfigValue();
+        }
+        return null;
     }
 
     /**
@@ -1374,7 +1401,7 @@
                             sysConfig = sysConfigMapper.checkConfigKeyUnique("wj.sms.template");
                         }
                         log.info("sysConfig鐨勫�间负锛歿}", sysConfig);
-                        if (!Objects.isNull(sysConfig)) {
+                        if (!Objects.isNull(sysConfig) && StringUtils.isNotEmpty(sysConfig.getConfigValue())) {
                             String smsContemt = sysConfig.getConfigValue();
 //                            sendMagParam.setContent("鎮ㄥソ锛岄個璇锋偍濉啓鍑洪櫌璋冩煡琛紝璇风偣鍑�" + sendMagParam.getUrl() + "濉啓銆傛劅璋㈡偍閰嶅悎锛�");
                             sendMagParam.setContent(smsContemt.replace("${wjurl}", sendMagParam.getUrl()));
@@ -1394,7 +1421,7 @@
                             sendMagParam.setUrl(url);
 
                             SysConfig sysConfig = sysConfigMapper.checkConfigKeyUnique("xj.sms.template");
-                            if (!Objects.isNull(sysConfig)) {
+                            if (!Objects.isNull(sysConfig) && StringUtils.isNotEmpty(sysConfig.getConfigValue())) {
                                 String smsContemt = sysConfig.getConfigValue();
 //                              sendMagParam.setContent("鎮ㄥソ锛岄個璇锋偍濉啓鍑洪櫌瀹f暀璋冩煡琛紝璇风偣鍑�" + sendMagParam.getUrl() + "鏌ョ湅銆傛劅璋㈡偍閰嶅悎锛�");
                                 sendMagParam.setContent(smsContemt.replace("${xjurl}", sendMagParam.getUrl()));
@@ -1410,6 +1437,13 @@
 
                     }
 
+                    //鍐呭涓虹┖璇存槑鐭俊妯℃澘鏈厤缃垨瀹f暀/閫氱煡鍐呭涓虹┖锛岀洿鎺ユ寜鍙戦�佸け璐ュ鐞嗭紝閬垮厤鍚庣画鍙戦�佹椂鍑虹幇绌烘寚閽�
+                    if (StringUtils.isEmpty(sendMagParam.getContent())) {
+                        log.error("銆恠fHandlle銆戦暱鏈熶换鍔$煭淇″唴瀹逛负绌猴紝鏃犳硶鍙戦�侊紝subid锛歿}锛宼askid锛歿}锛宼ype锛歿}锛屾満鏋処D锛歿}", serviceSubtask.getId(), serviceSubtask.getTaskid(), serviceSubtask.getType(), serviceSubtask.getOrgid());
+                        setSuccessPreachForm(serviceSubtask, sendPreachform, "闀挎湡浠诲姟鐭俊鍙戦�佸け璐�:鐭俊鍐呭涓虹┖(鐭俊妯℃澘鏈厤缃垨鍐呭涓虹┖), 鏃堕棿: " + now, "5");
+                        return;
+                    }
+
                     serviceOutPath.setRadix(format);
                     serviceOutPath.setUpdateTime(new Date());
                     serviceOutPath.setUrl(url);
@@ -1417,9 +1451,24 @@
                     //鐭俊鍙戦�侊細鍚勭珯鐐瑰鎺ユ柟寮忓樊寮傝緝澶э紙鎶ユ枃/绛惧悕/token/杩斿洖鍒ゅ畾閮戒笉鍚岋級锛�
                     //宸叉敹鍙e埌 ruoyi-message 妯″潡锛岃繖閲屽彧璐熻矗缁勮鍏ュ弬骞惰矾鐢�
                     log.info("active鐨勫�间负锛歿}", active);
-                    SendResult smsSendResult = sendSmsByActive(serviceSubtask, sendMagParam);
-                    log.info("smsSendResult鐨勫�间负锛歿}", smsSendResult);
-                    String isSuccess = smsSendResult.asLegacyFlag();
+                    String isSuccess = "";
+                    if ("ls".equals(active) && "47226079133110211G1001".equals(serviceSubtask.getOrgid())) {
+                        //涓芥按濡囧辜璧颁腑鍥界Щ鍔ㄤ簯MAS锛屼笉渚濊禆 ls.msg.info 涓� token锛�
+                        //ruoyi-message 鐨� ls 閫氶亾灏氭湭瑕嗙洊浜慚AS锛屾晠姝ゅ淇濈暀 SmsUtils 鐩磋繛
+                        SmsUtils.YunMasConfig yunMasConfig = SmsUtils.YunMasConfig.of(getConfigValue("ls.msg.yunmas", serviceSubtask.getOrgid()));
+                        SmsUtils.SmsResult yunMasSmsResult = SmsUtils.sendYunMasSms(yunMasConfig, sendMagParam.getPhone(), sendMagParam.getContent());
+                        log.info("銆恠fHandlle銆戜簯MAS鐭俊鍙戦�佺粨鏋滐細ok={}锛宺spcod={}锛宮sgGroup={}锛宮essage={}",
+                                yunMasSmsResult.isOk(), yunMasSmsResult.getRspcod(), yunMasSmsResult.getMsgGroup(), yunMasSmsResult.getMessage());
+                        isSuccess = yunMasSmsResult.isOk() ? "true" : "false";
+                        if (!yunMasSmsResult.isOk()) {
+                            log.error("銆恠fHandlle銆戜簯MAS鐭俊鍙戦�佸け璐ワ紝subid锛歿}锛屾墜鏈哄彿锛歿}锛屽師鍥狅細{}",
+                                    serviceSubtask.getId(), sendMagParam.getPhone(), yunMasSmsResult.getMessage());
+                        }
+                    } else {
+                        SendResult smsSendResult = sendSmsByActive(serviceSubtask, sendMagParam);
+                        log.info("smsSendResult鐨勫�间负锛歿}", smsSendResult);
+                        isSuccess = smsSendResult.asLegacyFlag();
+                    }
                     //浠诲姟鍙戦�佽褰�
                     ServiceSubtaskRecord serviceSubtaskRecord = new ServiceSubtaskRecord();
                     serviceSubtaskRecord.setTaskid(serviceSubtask.getTaskid().toString());
@@ -1487,6 +1536,7 @@
                     //浠诲姟鍙戦�佽褰�
                     ServiceSubtaskRecord serviceSubtaskRecord = new ServiceSubtaskRecord();
                     serviceSubtaskRecord.setTaskid(serviceSubtask.getTaskid().toString());
+                    serviceSubtaskRecord.setSubtaskId(serviceSubtask.getId());
                     serviceSubtaskRecord.setUuid(UUID.randomUUID().toString());
                     serviceSubtaskRecord.setTasktype(serviceSubtask.getType());
                     serviceSubtaskRecord.setPreachform("4");
@@ -1854,14 +1904,15 @@
      * @return
      */
     private String getObject(ServiceSubtask serviceSubtask, String scriptContent) {
+        //鍐呭涓虹┖鏃剁洿鎺ヨ繑鍥烇紝閬垮厤涓嬮潰鐨剅eplace鍑虹幇绌烘寚閽�
+        if (StringUtils.isEmpty(scriptContent)) {
+            return scriptContent;
+        }
         Map<String, Map<String, String>> param = getParam(serviceSubtask.getTaskid());
         for (Map<String, String> map : param.values()) {
             for (String key : map.keySet()) {
                 scriptContent = scriptContent.replace(key, StringUtils.isNotEmpty(map.get(key)) ? map.get(key) : "");
             }
-        }
-        if (StringUtils.isEmpty(scriptContent)) {
-            return scriptContent;
         }
         scriptContent = scriptContent.replace("${name}", StringUtils.isNotEmpty(serviceSubtask.getSendname()) ? serviceSubtask.getSendname() : "");
         scriptContent = scriptContent.replace("${dzz}", StringUtils.isNotEmpty(serviceSubtask.getAddr()) ? serviceSubtask.getAddr() : "");
@@ -1963,10 +2014,16 @@
             return false;
         }
         // 鍩哄噯鏃跺埢锛氬嚭闄㈡椂闂� + 鍙戦�佹棩锛坰endDay 缂虹渷鎸� 1锛夛紝骞跺厹搴曢伩鍏� NPE/鑴忔暟鎹�
+        if (serviceSubtask.getEndtime() == null) {
+            log.warn("銆恑sSubtaskDeadline銆憇ubid锛歿} endtime涓虹┖锛屾棤娉曡绠楄秴鏃讹紝鎸夋湭瓒呮椂澶勭悊", serviceSubtask.getId());
+            return false;
+        }
         ServiceTask serviceTask = serviceTaskMapper.selectServiceTaskByTaskid(serviceSubtask.getTaskid());
         int sendDay = serviceTask != null && serviceTask.getSendDay() != null ? serviceTask.getSendDay().intValue() : 1;
         Date deadline = DateUtils.addDays(serviceSubtask.getEndtime(), sendDay);
-        int sumCompensateTime = CollectionUtils.isEmpty(serviceSubtaskPreachforms) ? 0 : serviceSubtaskPreachforms.stream().mapToInt(x -> x.getCompensateTime() == null ? 0 : Integer.parseInt(x.getCompensateTime())).sum();
+        int sumCompensateTime = CollectionUtils.isEmpty(serviceSubtaskPreachforms) ? 0 : serviceSubtaskPreachforms.stream()
+                .mapToInt(x -> NumberUtils.toInt(x.getCompensateTime(), 0))
+                .sum();
         sumCompensateTime = sumCompensateTime + 1;
         // 鍙ˉ鍋挎渶鍚庝竴姝ヨ嚜韬殑琛ュ伩鏃堕棿锛堝ぉ锛夛紝鑴忓�间竴寰嬫寜 0 鍏滃簳
         deadline = DateUtils.addDays(deadline, sumCompensateTime);
@@ -1977,16 +2034,24 @@
     /**
      * 澶勭悊瓒呮椂闅忚浠诲姟锛堥殢璁胯秴鏃� 鈫� 鐢熸垚"鍐嶆闅忚"锛�
      * <p>
+     * 鎵弿銆屽嚭闄㈡弧 deadlineDay 澶╀粛鏈畬鎴愩�嶇殑棣栨闅忚浠诲姟锛坰endstate=7 + visit_count=1锛夛紝
+     * 鎸夊綋鍓嶄换鍔$殑 nexttaskid 鐢熸垚鍐嶆闅忚浠诲姟锛岀瀹ゅ浐瀹氫负闅忚涓績绉戝銆�
+     * <p>
+     * 娉ㄦ剰锛氭埅姝㈡椂闂村繀椤诲湪寰幆澶栬绠椾竴娆°�傚巻鍙插疄鐜版妸 {@code deadlineDay = -deadlineDay}
+     * 鍐欏湪寰幆浣撳唴锛屾瘡杞彇鍙嶄竴娆★紝瀵艰嚧鍋舵暟杞敤銆屼粖澶� + deadlineDay銆嶅仛鎴鏃堕棿锛�
+     * 鎶婂嚭闄笉婊� 28 澶╃殑瓒呮椂浠诲姟涔熸壂浜嗚繘鏉ャ��
+     * <p>
      * 鈿狅笍 2025 淇锛堝紑鍏冲け鏁堬級锛氭湰鏂规硶鍘熶笉鍒や换浣曟ā鍧楀紑鍏� 鈥斺��
      * 鍏虫帀 {@code inhospvisit.enabled} 鐓ф牱寤�"鍐嶆闅忚"瀛愪换鍔°��
      * <p>
      * <b>涓轰粈涔堣繖閲屾槸"鍏ュ彛鍒ゅ紑鍏�"鑰屼笉鏄儚 {@link #dealOutHospInfo()} 閭f牱鍒囧埌闂ㄩ潰</b>锛�
      * 妯″潡渚у彧鎻愪緵浜� {@code InhospVisitApi#buildAgainVisitDraft}锛堢粍瑁呰崏绋匡級锛�
-     * <b>娌℃湁</b>瀹屾暣鐨�"鎵弿瓒呮椂 鈫� 钀藉簱 鈫� 鍥炲啓鍘熶换鍔$姸鎬�"鍏ュ彛锛�
-     * 鑰屾湰鏂规硶鑷繁缁勮骞� insert锛堣涓嬫柟 {@code insertServiceSubtask}锛夈��
-     * 閫犱竴涓ā鍧楀叆鍙e睘浜庢柊澧炶兘鍔涖�佽秴鍑�"鍙В鍐冲紑鍏�"鐨勮寖鍥达紝鎵�浠ュ彇鏈�灏忔敼鍔細鍏ュ彛瀹堝崼銆�
+     * 瓒呮椂鎵弿涓庡洖鍐欏師浠诲姟鐘舵�佺殑鍏ュ彛鐢� service 渚ф壙鎺ワ紙瑙佷笅鏂� {@code addSubTaskAgainByDeadline}锛夈��
      * <p>
      * 鈿狅笍 鍏ㄤ粨鍙湁 {@code ServiceSubtaskController#dealSubtaskDeadline} 涓�涓皟鐢ㄨ�呫��
+     *
+     * @param orgid       鏈烘瀯ID
+     * @param deadlineDay 鍑洪櫌婊″灏戝ぉ绠楄秴鏃讹紙姝f暟锛屽 28锛�
      */
     public void dealSubtaskDeadline(String orgid, Integer deadlineDay) {
         // 鈿狅笍 寮�鍏冲畧鍗細鍏堝垽鍐嶅共娲伙紝涓� dealOutHospInfo / rollTaskGroupCycle 鍚屽彛寰勩��
@@ -2002,9 +2067,17 @@
         st.setNexttaskflag("1");
         st.setOrgid(orgid);
         List<ServiceTask> serviceTasks = serviceTaskMapper.selectServiceTaskList(st);
+
+        // 鎴鏃堕棿鍙畻涓�娆★細鍑洪櫌鏃堕棿 <= 浠婂ぉ - deadlineDay
+        int days = Math.abs(ObjectUtils.isEmpty(deadlineDay) ? 28 : deadlineDay);
+        Date deadline = DateUtils.addDays(new Date(), -days);
+        log.info("銆恉ealSubtaskDeadline銆戝紑濮嬫壂鎻�, orgid={}, day={}, deadline={}, 浠诲姟鏁�={}",
+                orgid, days, deadline, serviceTasks.size());
+
         for (ServiceTask serviceTask : serviceTasks) {
-            deadlineDay = -deadlineDay;
-            Date deadline = DateUtils.addDays(new Date(), deadlineDay);
+            if (ObjectUtils.isEmpty(serviceTask.getNexttaskid())) {
+                continue;
+            }
             //閫氳繃浠诲姟ID鎷垮埌鎮h�呬俊鎭�,骞朵笖闅忚鏃堕棿寰楁槸浠婂ぉ涔嬪墠鐨�
             ServiceSubtaskEntity serviceSubtaskVO = new ServiceSubtaskEntity();
             serviceSubtaskVO.setOrgid(orgid);
@@ -2013,44 +2086,19 @@
             serviceSubtaskVO.setVisitCount(1);
             serviceSubtaskVO.setTaskid(serviceTask.getTaskid());
             serviceSubtaskVO.setEndOutHospTime(deadline);
-            List<ServiceSubtask> serviceSubtaskList = serviceSubtaskMapper.selectServiceSubtaskList(serviceSubtaskVO);
+            List<ServiceSubtask> serviceSubtaskList
+                    = serviceSubtaskMapper.selectServiceSubtaskList(serviceSubtaskVO);
             log.info("銆恉ealSubtaskDeadline銆戣幏鍙栧埌{}涓秴鏃朵换鍔�, taskid={}", serviceSubtaskList.size(), serviceTask.getTaskid());
             for (ServiceSubtask serviceSubtask : serviceSubtaskList) {
-                PatMedInhosp patMedInhospVo = new PatMedInhosp();
-                patMedInhospVo.setOrgid(serviceSubtask.getOrgid());
-                patMedInhospVo.setInhospid(serviceSubtask.getInhospid());
-                patMedInhospVo.setCry(1);
-                List<PatMedInhosp> patMedInhospList = iPatMedInhospService.selectPatMedInhospList(patMedInhospVo);
-                PatMedInhosp patMedInhosp = new PatMedInhosp();
-                if (!patMedInhospList.isEmpty()) {
-                    patMedInhosp = patMedInhospList.get(0);
-                } else {
-                    continue;
-                }
-                if (ObjectUtils.isEmpty(serviceTask.getNexttaskid())) {
-                    continue;
-                }
-                ServiceTask nextServiceTask = serviceTaskMapper.selectServiceTaskByTaskid(serviceTask.getNexttaskid());
-                PatArchive patArchive = patArchiveService.selectPatArchiveByPatid(serviceSubtask.getPatid());
-                String config = configService.selectConfigByKey("visit.early.day");
-                Integer type = 1;
-
-                //鏂板鍐嶆闅忚浠诲姟
-                ServiceSubtask serviceSubtaskAgain = iPatMedInhospService.boxedServiceSubtask(nextServiceTask, patMedInhosp, patArchive, config, type);
-                serviceSubtaskAgain.setId(null);
-                serviceSubtaskAgain.setUpid(serviceSubtask.getId().intValue());
-                serviceSubtaskAgain.setVisitCount(serviceSubtask.getVisitCount() + 1);
-                serviceSubtaskAgain.setSendstate(2L);
-                serviceSubtaskAgain.setCreateTime(new Date());
-                serviceSubtaskAgain.setUpdateTime(new Date());
-                serviceSubtaskAgain.setVisitType(serviceSubtask.getVisitType());
-                serviceSubtaskAgain.setVisitDeptCode(serviceSubtask.getVisitDeptCode());
-                serviceSubtaskAgain.setVisitDeptName(serviceSubtask.getVisitDeptName());
-                int i = serviceSubtaskMapper.insertServiceSubtask(serviceSubtaskAgain);
-                if (i == 1) {
-                    //鍐嶆闅忚浠诲姟鍒涘缓鎴愬姛鍚庯紝鍘熶换鍔℃敼涓哄け璐ョ姸鎬�
-                    serviceSubtask.setSendstate(5L);
-                    serviceSubtaskMapper.updateServiceSubtask(serviceSubtask);
+                try {
+                    // 鐢熸垚閫昏緫缁熶竴鏀跺彛鍒� service锛氶攣 + 骞傜瓑 + 浜嬪姟 + 鍙戦�佹柟寮忔槑缁嗗垵濮嬪寲
+                    String result = iServiceSubtaskService.addSubTaskAgainByDeadline(serviceSubtask);
+                    log.info("銆恉ealSubtaskDeadline銆戠敓鎴愬啀娆¢殢璁�, subId={}, taskid={}, 缁撴灉={}",
+                            serviceSubtask.getId(), serviceTask.getTaskid(), result);
+                } catch (Exception e) {
+                    // 鍗曟潯澶辫触涓嶅奖鍝嶅叾浠栨偅鑰�
+                    log.error("銆恉ealSubtaskDeadline銆戠敓鎴愬啀娆¢殢璁垮け璐�, subId=" + serviceSubtask.getId()
+                            + ", taskid=" + serviceTask.getTaskid(), e);
                 }
             }
         }
diff --git a/ruoyi-quartz/src/main/resources/mapper/quartz/CollectHISMapper.xml b/ruoyi-quartz/src/main/resources/mapper/quartz/CollectHISMapper.xml
index f7e68b6..65e67c6 100644
--- a/ruoyi-quartz/src/main/resources/mapper/quartz/CollectHISMapper.xml
+++ b/ruoyi-quartz/src/main/resources/mapper/quartz/CollectHISMapper.xml
@@ -488,27 +488,41 @@
     </select>
 
     <select id="yhyksxx" parameterType="com.ruoyi.common.core.domain.entity.SysUser" resultMap="SysUserDeptResult">
-        select user_id , user_code , dept_type ,dept_code,dept_name,deptparent,del_flag,orgid
-        FROM healthy_user_dept
+        select ud.user_id , ud.user_code , ud.dept_type ,ud.dept_code,ud.dept_name,ud.deptparent,ud.del_flag,ud.orgid
+        FROM healthy_user_dept ud
         where 1=1
-        and user_id != 'admin'
-        and dept_type = '1'
+        and ud.user_id != 'admin'
+        and ud.dept_type = '1'
         <if test="orgid != null and orgid != ''">
-            AND orgid = #{orgid}
+            AND ud.orgid = #{orgid}
         </if>
-        <if test="hisUserId != null ">and user_id = #{hisUserId}</if>
+        <if test="hisUserId != null ">and ud.user_id = #{hisUserId}</if>
+        <if test="campusid != null and campusid != ''">
+            AND EXISTS (
+                SELECT 1 FROM healthy_dept d
+                WHERE d.his_dept_id = ud.dept_code
+                  AND d.orgid = #{campusid}
+            )
+        </if>
     </select>
 
     <select id="yhybfxx" parameterType="com.ruoyi.common.core.domain.entity.SysUser" resultMap="SysUserDeptResult">
-        select user_id , user_code , dept_type ,dept_code,dept_name,deptparent,del_flag,orgid
-        FROM healthy_user_dept
+        select ud.user_id , ud.user_code , ud.dept_type ,ud.dept_code,ud.dept_name,ud.deptparent,ud.del_flag,ud.orgid
+        FROM healthy_user_dept ud
         where 1=1
-        and user_id != 'admin'
-        and dept_type = '2'
+        and ud.user_id != 'admin'
+        and ud.dept_type = '2'
         <if test="orgid != null and orgid != ''">
-            AND orgid = #{orgid}
+            AND ud.orgid = #{orgid}
         </if>
-        <if test="hisUserId != null ">and user_id = #{hisUserId}</if>
+        <if test="hisUserId != null ">and ud.user_id = #{hisUserId}</if>
+        <if test="campusid != null and campusid != ''">
+            AND EXISTS (
+                SELECT 1 FROM healthy_dept d
+                WHERE d.his_dept_id = ud.dept_code
+                  AND d.orgid = #{campusid}
+            )
+        </if>
     </select>
 
     <select id="yhyyqxx" parameterType="com.ruoyi.common.core.domain.entity.SysUser" resultMap="SysUserOrgResult">
@@ -526,7 +540,7 @@
         <if test="hisUserId != null ">
             AND ud.user_id = #{hisUserId}
         </if>
-        <if test="campusid != null">
+        <if test="campusid != null and campusid != ''">
             AND d.orgid = #{campusid}
         </if>
         union all
@@ -544,7 +558,7 @@
         <if test="hisUserId != null ">
             AND udb.user_id = #{hisUserId}
         </if>
-        <if test="campusid != null">
+        <if test="campusid != null and campusid != ''">
             AND d.orgid = #{campusid}
         </if>
     </select>
@@ -769,6 +783,30 @@
             <if test="orgid != null and orgid != ''">
                 AND u.orgid = #{orgid}
             </if>
+            <if test="lastStartTime != null and lastStartTime != ''">
+                AND u.last_Update_Time &gt;= to_date(#{lastStartTime}, 'YYYY-MM-DD HH24:MI:SS')
+            </if>
+            <if test="lastEndTime != null and lastEndTime != ''">
+                AND u.last_Update_Time &lt;= to_date(#{lastEndTime}, 'YYYY-MM-DD HH24:MI:SS')
+            </if>
+            <if test="campusid != null and campusid != ''">
+                AND (
+                    EXISTS (
+                        SELECT 1
+                        FROM healthy_user_dept ud
+                        JOIN healthy_dept d ON d.his_dept_id = ud.dept_code
+                        WHERE ud.user_id = u.his_user_id
+                          AND d.orgid = #{campusid}
+                    )
+                    OR EXISTS (
+                        SELECT 1
+                        FROM healthy_user_dept_bf udb
+                        JOIN healthy_dept d2 ON d2.his_dept_id = udb.dept_code
+                        WHERE udb.user_id = u.his_user_id
+                          AND d2.orgid = #{campusid}
+                    )
+                )
+            </if>
             <if test="userId != null and userId != 0">
                 AND u.user_id = #{userId}
             </if>
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserDeptMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserDeptMapper.java
index 88562e9..fc14677 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserDeptMapper.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserDeptMapper.java
@@ -3,6 +3,7 @@
 import com.ruoyi.common.core.domain.entity.SysDept;
 import com.ruoyi.common.core.domain.entity.SysUserDept;
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
 import org.springframework.stereotype.Component;
 
 import java.util.List;
@@ -77,4 +78,15 @@
     public int deleteSysUserDeptByUserId(SysUserDept sysUserDept);
 
     public int updateSysUserDeptForSync(SysUserDept sysUserDept);
+
+    /**
+     * 鏍规嵁鐢ㄦ埛ID銆侀儴闂ㄧ紪鐮佹煡璇㈢敤鎴烽儴闂ㄥ叧绯伙紙鍖呭惈宸查�昏緫鍒犻櫎鐨勬暟鎹級
+     * 涓� hosp_info / dept_info 鐨勭敓鎴愰�昏緫淇濇寔涓�鑷达紝閮ㄩ棬缂栫爜鏄敤鎴烽儴闂ㄥ叧绯荤殑涓氬姟鍞竴閿�
+     *
+     * @param userId   鐢ㄦ埛ID
+     * @param deptCode 閮ㄩ棬缂栫爜
+     * @param orgid    鏈烘瀯ID锛屽彲涓虹┖
+     * @return 鐢ㄦ埛閮ㄩ棬鍏崇郴
+     */
+    public SysUserDept selectSysUserDeptByUserAndCode(@Param("userId") Long userId, @Param("deptCode") String deptCode, @Param("orgid") String orgid);
 }
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java
index 8ae32f4..3b0c841 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java
@@ -24,6 +24,15 @@
     public List<SysUser> getUserList(SysUser sysUser);
 
     /**
+     * 鏌ヨ闇�瑕佹牴鎹� hosp_info / dept_info 琛ュ叏閮ㄩ棬鍏崇郴鐨勭敤鎴�
+     *
+     * @param orgid  鏈烘瀯ID锛屽彲涓虹┖
+     * @param userId 鐢ㄦ埛ID锛屽彲涓虹┖
+     * @return 鐢ㄦ埛淇℃伅闆嗗悎
+     */
+    public List<SysUser> selectUsersForRepairUserDept(@Param("orgid") String orgid, @Param("userId") Long userId);
+
+    /**
      * 鏍规嵁鏉′欢鍒嗛〉鏌ヨ宸查厤鐢ㄦ埛瑙掕壊鍒楄〃
      *
      * @param user 鐢ㄦ埛淇℃伅
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/ext/SysDept2Mapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/ext/SysDept2Mapper.java
index 3fa48bd..8e3c311 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/ext/SysDept2Mapper.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/ext/SysDept2Mapper.java
@@ -40,6 +40,11 @@
 
     public SysDept selectDeptByCode(@Param("deptCode")String deptCode,@Param("orgid")String orgid);
 
+    /**
+     * 鎸夌瀹ょ紪鐮併�佹満鏋勩�佺瀹ょ被鍨嬫煡璇紙绉戝鍜岀梾鍖哄彲鑳戒娇鐢ㄧ浉鍚岀紪鐮侊紝蹇呴』甯� deptType 绮剧‘瀹氫綅锛�
+     */
+    public SysDept selectDeptByCodeAndType(@Param("deptCode")String deptCode,@Param("orgid")String orgid,@Param("deptType")String deptType);
+
     public SysDept selectDeptByCodeAndCampus(@Param("deptCode")String deptCode,@Param("orgid")String orgid,@Param("campusid")String campusid);
 
     /**
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java
index 16cad1c..5718202 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java
@@ -1,6 +1,7 @@
 package com.ruoyi.system.service;
 
 import java.util.List;
+import java.util.Map;
 import com.ruoyi.common.core.domain.entity.SysUser;
 
 /**
@@ -214,4 +215,13 @@
      * @return 缁撴灉
      */
     public String importUser(List<SysUser> userList, Boolean isUpdateSupport, String operName);
+
+    /**
+     * 鏍规嵁 sys_user.hosp_info锛堢瀹わ級鍜� sys_user.dept_info锛堢梾鍖猴級琛ュ叏 sys_user_dept 鏁版嵁
+     *
+     * @param orgid  鏈烘瀯ID锛屼负绌烘椂澶勭悊鍏ㄩ儴鏈烘瀯
+     * @param userId 鐢ㄦ埛ID锛屼负绌烘椂澶勭悊鍏ㄩ儴鐢ㄦ埛
+     * @return 澶勭悊缁撴灉缁熻
+     */
+    Map<String, Object> repairUserDeptByHospAndDeptInfo(String orgid, Long userId);
 }
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java
index 6db8841..213b376 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java
@@ -335,8 +335,7 @@
     public List<SysDept> selectLeafSysDeptListBySysUserDept(SysUserDept sysUserDept){
         List<SysDept> list =  deptMapper.selectSysDeptListBySysUserDept(sysUserDept);
         if(CollectionUtils.isNotEmpty(list)){
-            List<SysDept> tree = TreeUtils.buildDeptTree(list);
-            list = TreeUtils.getLeafDepts(tree);
+            list = TreeUtils.getLeafDepts(list);
         }
         return list;
     }
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java
index 20f6318..29b434b 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java
@@ -8,16 +8,20 @@
 import com.ruoyi.common.utils.DtoConversionUtils;
 import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.TreeUtils;
 import com.ruoyi.common.utils.bean.BeanValidators;
 import com.ruoyi.common.utils.spring.SpringUtils;
 import com.ruoyi.system.domain.SysPost;
 import com.ruoyi.system.domain.SysUserPost;
 import com.ruoyi.system.mapper.*;
 import com.ruoyi.system.service.ISysConfigService;
+import com.ruoyi.system.service.ISysDeptService;
 import com.ruoyi.system.service.ISysUserService;
 import com.ruoyi.common.core.service.IUserExtInfoService;
 import com.ruoyi.common.core.service.ISatisfactionCategoryService;
 import com.ruoyi.system.mapper.SysUserDeptMapper;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -44,8 +48,13 @@
 public class SysUserServiceImpl implements ISysUserService {
     private static final Logger log = LoggerFactory.getLogger(SysUserServiceImpl.class);
 
+    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
     @Autowired
     private SysUserMapper userMapper;
+
+    @Autowired
+    private ISysDeptService sysDeptService;
 
     @Autowired
     private SysDeptMapper sysDeptMapper;
@@ -144,15 +153,15 @@
         List<SysUserDept> sysUserDeptBQs = null;
         if (StringUtils.isNotEmpty(sysUser.getSearchscope()) && sysUser.getSearchscope().equals("0")) {
             sysUserDept.setDeptType("1");
-            sysUserDeptKSs = sysUserDeptMapper.selectSysUserDeptList(sysUserDept);
+            sysUserDeptKSs = selectLeafSysDeptList(sysUserDept);
             sysUserDept.setDeptType("2");
-            sysUserDeptBQs = sysUserDeptMapper.selectSysUserDeptList(sysUserDept);
+            sysUserDeptBQs = selectLeafSysDeptList(sysUserDept);
         } else if (StringUtils.isNotEmpty(sysUser.getSearchscope()) && sysUser.getSearchscope().equals("1")) {
             sysUserDept.setDeptType("1");
-            sysUserDeptKSs = sysUserDeptMapper.selectSysUserDeptList(sysUserDept);
+            sysUserDeptKSs = selectLeafSysDeptList(sysUserDept);
         } else if (StringUtils.isNotEmpty(sysUser.getSearchscope()) && sysUser.getSearchscope().equals("2")) {
             sysUserDept.setDeptType("2");
-            sysUserDeptBQs = sysUserDeptMapper.selectSysUserDeptList(sysUserDept);
+            sysUserDeptBQs = selectLeafSysDeptList(sysUserDept);
         }
 
         sysUser.setDepts(deptIds);
@@ -246,8 +255,8 @@
                     SysDept deptBQ = new SysDept();
                     deptBQ.setOrgid(split[1]);
                     deptBQ.setDeptType("2");
-                    CompletableFuture<List<SysDept>> ksFuture = CompletableFuture.supplyAsync(() -> sysDeptMapper.selectDeptList(deptKS), executor);
-                    CompletableFuture<List<SysDept>> bqFuture = CompletableFuture.supplyAsync(() -> sysDeptMapper.selectDeptList(deptBQ), executor);
+                    CompletableFuture<List<SysDept>> ksFuture = CompletableFuture.supplyAsync(() -> selectLeafSysDeptForAdmin(deptKS), executor);
+                    CompletableFuture<List<SysDept>> bqFuture = CompletableFuture.supplyAsync(() -> selectLeafSysDeptForAdmin(deptBQ), executor);
                     try {
                         sysUserDeptKSs = distinctByDeptCode(DtoConversionUtils.sourceToTarget(ksFuture.get(5, TimeUnit.SECONDS), SysUserDept.class));
                         sysUserDeptBQs = distinctByDeptCode(DtoConversionUtils.sourceToTarget(bqFuture.get(5, TimeUnit.SECONDS), SysUserDept.class));
@@ -264,8 +273,8 @@
                     queryBQ.setUserId(sysUser.getUserId());
                     queryBQ.setDeptType("2");
                     queryBQ.setOrgid(split[1]);
-                    CompletableFuture<List<SysUserDept>> ksFuture = CompletableFuture.supplyAsync(() -> sysUserDeptMapper.selectSysUserDeptList(queryKS), executor);
-                    CompletableFuture<List<SysUserDept>> bqFuture = CompletableFuture.supplyAsync(() -> sysUserDeptMapper.selectSysUserDeptList(queryBQ), executor);
+                    CompletableFuture<List<SysUserDept>> ksFuture = CompletableFuture.supplyAsync(() -> selectLeafSysDeptList(queryKS), executor);
+                    CompletableFuture<List<SysUserDept>> bqFuture = CompletableFuture.supplyAsync(() -> selectLeafSysDeptList(queryBQ), executor);
                     try {
                         sysUserDeptKSs = ksFuture.get(5, TimeUnit.SECONDS);
                         sysUserDeptBQs = bqFuture.get(5, TimeUnit.SECONDS);
@@ -278,13 +287,13 @@
                 queryKS.setUserId(sysUser.getUserId());
                 queryKS.setDeptType("1");
                 queryKS.setOrgid(split[1]);
-                sysUserDeptKSs = sysUserDeptMapper.selectSysUserDeptList(queryKS);
+                sysUserDeptKSs = selectLeafSysDeptList(queryKS);
             } else if ("2".equals(sysUser.getSearchscope())) {
                 SysUserDept queryBQ = new SysUserDept();
                 queryBQ.setUserId(sysUser.getUserId());
                 queryBQ.setDeptType("2");
                 queryBQ.setOrgid(split[1]);
-                sysUserDeptBQs = sysUserDeptMapper.selectSysUserDeptList(queryBQ);
+                sysUserDeptBQs = selectLeafSysDeptList(queryBQ);
             }
         }
 
@@ -412,20 +421,20 @@
                 sysUserDept.setUserId(null);
             }
             sysUserDept.setDeptType("1");
-            sysUserDeptKSs = sysUserDeptMapper.selectSysUserDeptList(sysUserDept);
+            sysUserDeptKSs = selectLeafSysDeptList(sysUserDept);
             sysUserDept.setDeptType("2");
-            sysUserDeptBQs = sysUserDeptMapper.selectSysUserDeptList(sysUserDept);
+            sysUserDeptBQs = selectLeafSysDeptList(sysUserDept);
             //濡傛灉鏄痑dmin锛岄渶瑕佸sysUserDeptBQs锛宻ysUserDeptBQs鍘婚噸
             if (StringUtils.isNotEmpty(split[0]) && split[0].equals("admin")) {
                 //濡傛灉鏄痑dmin锛屽彧鑳藉幓sys_dept鍘诲彇鏁版嵁
                 SysDept dept = new SysDept();
                 dept.setOrgid(split[1]);
                 dept.setDeptType("1");
-                sysDepts = sysDeptMapper.selectDeptList(dept);
+                sysDepts = selectLeafSysDeptForAdmin(dept);
                 sysUserDeptKSs = DtoConversionUtils.sourceToTarget(sysDepts, SysUserDept.class);
 
                 dept.setDeptType("2");
-                sysDepts = sysDeptMapper.selectDeptList(dept);
+                sysDepts = selectLeafSysDeptForAdmin(dept);
                 sysUserDeptBQs = DtoConversionUtils.sourceToTarget(sysDepts, SysUserDept.class);
 
                 List<SysUserDept> distinctUserDepts = new ArrayList<>();
@@ -1010,4 +1019,210 @@
         }
         return successMsg.toString();
     }
+
+    public List<SysUserDept> selectLeafSysDeptList(SysUserDept sysUserDept){
+        List<SysDept> list =  sysDeptMapper.selectSysDeptListBySysUserDept(sysUserDept);
+        if(org.apache.commons.collections4.CollectionUtils.isNotEmpty(list)){
+            list = TreeUtils.getLeafDepts(list);
+        }
+        List<SysUserDept> list1 = DtoConversionUtils.sourceToTarget(list, SysUserDept.class);
+        return list1;
+    }
+
+    public List<SysDept> selectLeafSysDeptForAdmin(SysDept sysDept){
+        SysUserDept sysUserDept = DtoConversionUtils.sourceToTarget(sysDept, SysUserDept.class);
+        List<SysDept> list =  sysDeptMapper.selectSysDeptListBySysUserDept(sysUserDept);
+        if(org.apache.commons.collections4.CollectionUtils.isNotEmpty(list)){
+            list = TreeUtils.getLeafDepts(list);
+        }
+        return list;
+    }
+
+    /**
+     * 鏍规嵁 sys_user.hosp_info锛堢瀹わ級鍜� sys_user.dept_info锛堢梾鍖猴級琛ュ叏 sys_user_dept 鏁版嵁
+     *
+     * @param orgid  鏈烘瀯ID锛屼负绌烘椂澶勭悊鍏ㄩ儴鏈烘瀯
+     * @param userId 鐢ㄦ埛ID锛屼负绌烘椂澶勭悊鍏ㄩ儴鐢ㄦ埛
+     * @return 澶勭悊缁撴灉缁熻
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Map<String, Object> repairUserDeptByHospAndDeptInfo(String orgid, Long userId) {
+        List<SysUser> users = userMapper.selectUsersForRepairUserDept(orgid, userId);
+        int processedUsers = 0;
+        int inserted = 0;
+        int updated = 0;
+        int restored = 0;
+        int deptNotFound = 0;
+        Set<String> missingDeptCodes = new LinkedHashSet<>();
+
+        for (SysUser user : users) {
+            if (user.getUserId() == null) {
+                continue;
+            }
+            List<String> hospCodes = parseDeptCodes(user.getHospInfo());
+            List<String> wardCodes = parseDeptCodes(user.getDeptInfo());
+            if (hospCodes.isEmpty() && wardCodes.isEmpty()) {
+                continue;
+            }
+            processedUsers++;
+
+            int[] hospStat = repairUserDept(user, hospCodes, "1", missingDeptCodes);
+            int[] wardStat = repairUserDept(user, wardCodes, "2", missingDeptCodes);
+            inserted += hospStat[0] + wardStat[0];
+            updated += hospStat[1] + wardStat[1];
+            restored += hospStat[2] + wardStat[2];
+            deptNotFound += hospStat[3] + wardStat[3];
+        }
+
+        Map<String, Object> result = new LinkedHashMap<>();
+        result.put("scannedUsers", users.size());
+        result.put("processedUsers", processedUsers);
+        result.put("inserted", inserted);
+        result.put("updated", updated);
+        result.put("restored", restored);
+        result.put("deptMetaNotFound", deptNotFound);
+        result.put("missingDeptCodes", missingDeptCodes);
+        log.info("鏍规嵁hosp_info/dept_info琛ュ叏鐢ㄦ埛閮ㄩ棬鍏崇郴瀹屾垚锛岀粨鏋滐細{}", result);
+        return result;
+    }
+
+    /**
+     * 琛ュ叏鍗曚釜鐢ㄦ埛鎸囧畾绫诲瀷鐨勯儴闂ㄥ叧绯汇��
+     * hosp_info / dept_info 鐢� Gson 搴忓垪鍖� List&lt;List&lt;String&gt;&gt; 鐢熸垚锛�
+     * 姣忎釜鍏冪礌褰㈠ [["code"]]锛屽洜姝や互閮ㄩ棬缂栫爜浣滀负琛ュ叏渚濇嵁锛屼笌鍘熷閲囬泦閫昏緫涓�鑷淬��
+     *
+     * @param user             鐢ㄦ埛
+     * @param deptCodes        閮ㄩ棬缂栫爜闆嗗悎
+     * @param expectedDeptType 閮ㄩ棬绫诲瀷锛�1绉戝 2鐥呭尯锛屽搴� hosp_info / dept_info锛�
+     * @param missingDeptCodes sys_dept 涓湭鍖归厤鍒扮殑閮ㄩ棬缂栫爜
+     * @return [鏂板鏁�, 鏇存柊鏁�, 鎭㈠鏁�, sys_dept鏈尮閰嶆暟]
+     */
+    private int[] repairUserDept(SysUser user, List<String> deptCodes, String expectedDeptType, Set<String> missingDeptCodes) {
+        int[] stat = new int[4];
+        for (String deptCode : deptCodes) {
+            // sys_dept 鍙敤浜庤ˉ鍏� dept_id銆乨ept_name 绛変俊鎭紝鏌ヤ笉鍒颁篃缁х画鎸夌紪鐮佽ˉ鍏�
+            SysDept sysDept = sysDeptMapper.selectDeptByCode(expectedDeptType, user.getOrgid(), deptCode);
+            if (ObjectUtils.isEmpty(sysDept)) {
+                // dept_type 鍙兘瀛樺湪鍘嗗彶鑴忔暟鎹紝鎸夌紪鐮佸厹搴曞啀鏌ヤ竴娆�
+                sysDept = sysDeptMapper.selectDeptByCode(null, user.getOrgid(), deptCode);
+            }
+            if (ObjectUtils.isEmpty(sysDept)) {
+                stat[3]++;
+                if (missingDeptCodes.size() < 100) {
+                    missingDeptCodes.add(deptCode);
+                }
+            }
+
+            String userDeptOrgid = StringUtils.isNotEmpty(user.getOrgid()) ? user.getOrgid()
+                    : (sysDept == null ? null : sysDept.getOrgid());
+            String campusid = sysDept != null && StringUtils.isNotEmpty(sysDept.getCampusid())
+                    ? sysDept.getCampusid() : user.getCampusid();
+            Date now = new Date();
+
+            // 涓庡師濮嬬敓鎴愰�昏緫淇濇寔涓�鑷达紝user_id + dept_code + orgid 浣滀负涓氬姟鍞竴閿�
+            SysUserDept sysUserDept = sysUserDeptMapper.selectSysUserDeptByUserAndCode(user.getUserId(), deptCode, user.getOrgid());
+            if (ObjectUtils.isEmpty(sysUserDept)) {
+                sysUserDept = new SysUserDept();
+                sysUserDept.setUserId(user.getUserId());
+                sysUserDept.setDeptCode(deptCode);
+                sysUserDept.setDeptType(expectedDeptType);
+                sysUserDept.setOrgid(userDeptOrgid);
+                sysUserDept.setCampusid(campusid);
+                if (sysDept != null) {
+                    sysUserDept.setDeptId(sysDept.getDeptId());
+                    sysUserDept.setDeptName(sysDept.getDeptName());
+                }
+                sysUserDept.setDelFlag(0L);
+                sysUserDept.setCreateTime(now);
+                sysUserDept.setUpdateTime(now);
+                sysUserDeptMapper.insertSysUserDept(sysUserDept);
+                stat[0]++;
+                continue;
+            }
+
+            boolean wasDeleted = !Long.valueOf(0L).equals(sysUserDept.getDelFlag());
+            sysUserDept.setDeptCode(deptCode);
+            sysUserDept.setDeptType(expectedDeptType);
+            sysUserDept.setOrgid(userDeptOrgid);
+            sysUserDept.setCampusid(campusid);
+            if (sysDept != null) {
+                sysUserDept.setDeptId(sysDept.getDeptId());
+                sysUserDept.setDeptName(sysDept.getDeptName());
+            }
+            sysUserDept.setDelFlag(0L);
+            sysUserDept.setUpdateTime(now);
+            sysUserDeptMapper.updateSysUserDept(sysUserDept);
+            if (wasDeleted) {
+                stat[2]++;
+            } else {
+                stat[1]++;
+            }
+        }
+        return stat;
+    }
+
+    /**
+     * 瑙f瀽 hosp_info / dept_info 涓殑閮ㄩ棬缂栫爜銆�
+     * 鍘熷鐢熸垚閫昏緫浣跨敤 Gson 搴忓垪鍖� List&lt;List&lt;String&gt;&gt;锛屾牸寮忓 [["code1"],["code2"]]锛�
+     * 杩欓噷鍏煎璇ユ牸寮忥紝鍚屾椂鍏煎 ["code"]銆乕{"deptCode":"code"}] 绛夊巻鍙叉牸寮忋��
+     */
+    private List<String> parseDeptCodes(String json) {
+        if (StringUtils.isEmpty(json)) {
+            return Collections.emptyList();
+        }
+        List<String> codes = new ArrayList<>();
+        try {
+            collectDeptCodes(OBJECT_MAPPER.readTree(json), codes);
+        } catch (Exception e) {
+            log.warn("瑙f瀽鐢ㄦ埛绉戝/鐥呭尯淇℃伅澶辫触锛屽唴瀹癸細{}", json, e);
+        }
+        return codes.stream().filter(StringUtils::isNotEmpty).distinct().collect(Collectors.toList());
+    }
+
+    private void collectDeptCodes(JsonNode node, List<String> codes) {
+        if (node == null || node.isNull()) {
+            return;
+        }
+        if (node.isArray()) {
+            for (JsonNode item : node) {
+                collectDeptCodes(item, codes);
+            }
+            return;
+        }
+        if (node.isObject()) {
+            JsonNode codeNode = node.get("deptCode");
+            if (codeNode == null) {
+                codeNode = node.get("code");
+            }
+            if (codeNode == null) {
+                codeNode = node.get("deptId");
+            }
+            if (codeNode == null) {
+                codeNode = node.get("hisDeptId");
+            }
+            if (codeNode != null) {
+                collectDeptCodes(codeNode, codes);
+                return;
+            }
+            Iterator<Map.Entry<String, JsonNode>> fields = node.fields();
+            while (fields.hasNext()) {
+                JsonNode value = fields.next().getValue();
+                if (value != null && value.isValueNode() && !value.isNull()) {
+                    collectDeptCodes(value, codes);
+                    return;
+                }
+            }
+            return;
+        }
+        if (node.isValueNode()) {
+            String code = node.asText();
+            if (StringUtils.isNotEmpty(code)) {
+                code = code.trim();
+                if (StringUtils.isNotEmpty(code) && !"null".equalsIgnoreCase(code)) {
+                    codes.add(code);
+                }
+            }
+        }
+    }
 }
diff --git a/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml
index 1b37417..225eac3 100644
--- a/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml
+++ b/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml
@@ -135,6 +135,7 @@
         select d.dept_id,
                d.parent_id,
                d.campusid,
+               d.orgid,
                d.dept_code,
                d.dept_type,
                d.ancestors,
diff --git a/ruoyi-system/src/main/resources/mapper/system/SysUserDeptMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysUserDeptMapper.xml
index 4795ebc..88c3340 100644
--- a/ruoyi-system/src/main/resources/mapper/system/SysUserDeptMapper.xml
+++ b/ruoyi-system/src/main/resources/mapper/system/SysUserDeptMapper.xml
@@ -82,6 +82,17 @@
         where id = #{id}
     </select>
 
+    <select id="selectSysUserDeptByUserAndCode" resultMap="SysUserDeptResult">
+        <include refid="selectSysUserDeptVo"/>
+        where user_id = #{userId}
+          and dept_code = #{deptCode}
+        <if test="orgid != null and orgid != ''">
+            and orgid = #{orgid}
+        </if>
+        order by del_flag asc, id asc
+        limit 1
+    </select>
+
     <insert id="insertSysUserDept" parameterType="com.ruoyi.common.core.domain.entity.SysUserDept"
             useGeneratedKeys="true" keyProperty="id">
         insert into sys_user_dept
diff --git a/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml
index f85b5ab..ac40ec5 100644
--- a/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml
+++ b/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml
@@ -245,6 +245,26 @@
         <if test="pageSize != null  and pageNum != null">limit ${pageSize} OFFSET ${pageNum}</if>
     </select>
 
+    <select id="selectUsersForRepairUserDept" resultMap="SysUserResult">
+        select u.user_id,
+               u.user_name,
+               u.orgid,
+               u.campusid,
+               u.hosp_info,
+               u.dept_info
+        from sys_user u
+        where u.del_flag = '0'
+          and ((u.hosp_info is not null and u.hosp_info != '' and u.hosp_info != '[]')
+            or (u.dept_info is not null and u.dept_info != '' and u.dept_info != '[]'))
+        <if test="orgid != null and orgid != ''">
+            AND u.orgid = #{orgid}
+        </if>
+        <if test="userId != null">
+            AND u.user_id = #{userId}
+        </if>
+        order by u.user_id
+    </select>
+
     <select id="selectAllocatedList" parameterType="com.ruoyi.common.core.domain.entity.SysUser"
             resultMap="SysUserResult">
         select distinct u.user_id,u.his_user_id,u.title,u.hosp_info,u.dept_info, u.searchscope, u.dept_id, u.user_name,
diff --git a/ruoyi-system/src/main/resources/mapper/system/ext/SysDept2Mapper.xml b/ruoyi-system/src/main/resources/mapper/system/ext/SysDept2Mapper.xml
index 7a12eb3..8be92e7 100644
--- a/ruoyi-system/src/main/resources/mapper/system/ext/SysDept2Mapper.xml
+++ b/ruoyi-system/src/main/resources/mapper/system/ext/SysDept2Mapper.xml
@@ -152,6 +152,22 @@
         <if test="orgid != null and orgid != ''">
             AND orgid = #{orgid}
         </if>
+        order by d.dept_id
+        limit 1
+    </select>
+
+    <select id="selectDeptByCodeAndType" resultMap="SysDeptResult">
+        <include refid="selectDeptVo"/>
+        where d.del_flag = '0'
+        and d.dept_code = #{deptCode}
+        <if test="orgid != null and orgid != ''">
+            AND d.orgid = #{orgid}
+        </if>
+        <if test="deptType != null and deptType != ''">
+            AND d.dept_type = #{deptType}
+        </if>
+        order by d.dept_id
+        limit 1
     </select>
 
     <select id="selectDeptByCodeAndCampus" parameterType="string" resultMap="SysDeptResult">
diff --git a/ruoyi-task/src/main/java/com/ruoyi/task/common/ScheduleSubtaskBuilder.java b/ruoyi-task/src/main/java/com/ruoyi/task/common/ScheduleSubtaskBuilder.java
index fe9f494..ab115a0 100644
--- a/ruoyi-task/src/main/java/com/ruoyi/task/common/ScheduleSubtaskBuilder.java
+++ b/ruoyi-task/src/main/java/com/ruoyi/task/common/ScheduleSubtaskBuilder.java
@@ -79,7 +79,16 @@
 
             // 妯℃澘锛歞etail 浼樺厛浜� task
             if (detail.getTemplateid() != null) {
-                subtask.setTemplateid(detail.getTemplateid());
+                // 鍏煎鍘嗗彶鏁版嵁锛氭槑缁� templateid 鍙兘鏄ā鏉垮簱ID锛屼笉鑳界洿鎺ヤ綔涓轰换鍔℃ā鏉縄D浣跨敤銆�
+                if ("2".equals(serviceTask.getType())
+                        && String.valueOf(detail.getTemplateid()).equals(serviceTask.getLibtemplateid())
+                        && serviceTask.getTemplateid() != null) {
+                    log.warn("璁″垝鏄庣粏 templateid [{}] 鏄ā鏉垮簱ID锛屽凡鍥為��涓轰换鍔℃ā鏉縄D [{}], taskid={}, detailId={}",
+                            detail.getTemplateid(), serviceTask.getTemplateid(), serviceTask.getTaskid(), detail.getId());
+                    subtask.setTemplateid(serviceTask.getTemplateid());
+                } else {
+                    subtask.setTemplateid(detail.getTemplateid());
+                }
                 subtask.setTemplatename(detail.getTemplatename());
             }
             if (detail.getLibtemplateid() != null) {
diff --git a/ruoyi-task/src/main/java/com/ruoyi/task/controller/ServiceSubtaskController.java b/ruoyi-task/src/main/java/com/ruoyi/task/controller/ServiceSubtaskController.java
index 78393a6..ac3f09a 100644
--- a/ruoyi-task/src/main/java/com/ruoyi/task/controller/ServiceSubtaskController.java
+++ b/ruoyi-task/src/main/java/com/ruoyi/task/controller/ServiceSubtaskController.java
@@ -364,8 +364,7 @@
     public AjaxResult querySubtaskList(@RequestBody ServiceSubtaskVO serviceSubtaskVO) {
         ServiceSubtaskEntity serviceSubtaskEntity = DtoConversionUtils.sourceToTarget(serviceSubtaskVO, ServiceSubtaskEntity.class);
         serviceSubtaskEntity.setContinueContent(ObjectUtils.isNotEmpty(serviceSubtaskVO.getContinueContent()) ? serviceSubtaskVO.getContinueContent().toString() : null);
-        PageUtils.startPageByPost(serviceSubtaskEntity.getPageNum(), serviceSubtaskEntity.getPageSize());
-        serviceSubtaskService.selectServiceSubtaskList(serviceSubtaskEntity);
+        // 璇� SQL 宸插唴缃� limit ${pageSize} OFFSET ${pageNum}锛屼笉鍐嶈蛋 PageHelper锛岄伩鍏嶄袱濂楀垎椤靛彔鍔犱骇鐢熶袱涓� LIMIT
         return success(serviceSubtaskService.selectServiceSubtaskList(serviceSubtaskEntity));
     }
 
@@ -379,8 +378,8 @@
     public TableDataInfo querySubtaskListAgain(@RequestBody ServiceSubtaskVO serviceSubtaskVO) {
         ServiceSubtaskEntity serviceSubtaskEntity = DtoConversionUtils.sourceToTarget(serviceSubtaskVO, ServiceSubtaskEntity.class);
         serviceSubtaskEntity.setContinueContent(ObjectUtils.isNotEmpty(serviceSubtaskVO.getContinueContent()) ? serviceSubtaskVO.getContinueContent().toString() : null);
+        // 璇� SQL 鏈唴缃� limit锛屼娇鐢� PageHelper 鍒嗛〉锛涘彧璋冪敤涓�娆★紝getDataTable 鍙熀浜� Page 鍙栧埌姝g‘ total
         PageUtils.startPageByPost(serviceSubtaskEntity.getPageNum(), serviceSubtaskEntity.getPageSize());
-        serviceSubtaskService.selectServiceSubtaskListAgain(serviceSubtaskEntity);
         return getDataTable(serviceSubtaskService.selectServiceSubtaskListAgain(serviceSubtaskEntity));
     }
 
@@ -693,6 +692,10 @@
      * manualInfo - visitCount = 1 && currentPreachForm = "1"
      * voiceInfo - visitCount = 1 && currentPreachForm = "3"
      * smsInfo - visitCount = 1 && currentPreachForm = "4"
+     * manualSuccessInfo - visitCount = 1 && currentPreachForm = "1" && sendstate = 6
+     * voiceSuccessInfo - visitCount = 1 && currentPreachForm = "3" && sendstate = 6
+     * smsSuccessInfo - visitCount = 1 && currentPreachForm = "4" && sendstate = 6
+     * wechatSuccessInfo - visitCount = 1 && currentPreachForm = "5" && sendstate = 6
      * wechatInfo - visitCount = 1 && currentPreachForm = "5"
      * abnormalInfo - visitCount = 1 && excep = "1"
      * needFollowUpAgainInfo - visitCount > 1 && sendstate != 4
@@ -710,6 +713,19 @@
      * taskSituation4Info - taskSituation = 4
      * taskSituation5Info - taskSituation = 5
      * taskSituation6Info - taskSituation = 7
+     * manuallyCalledInfo - isManuallyCalled = 1L
+     * manualCallTimeoutInfo - visitCount = 1 && sendstate = 7 && isManuallyCalled = 0
+     * manualCallTimeoutAgainInfo - visitCount > 1 && sendstate = 7 && isManuallyCalled = 0
+     * pendingFollowUpAllInfo - visitCount = 1 && sendstate in (1,2,3,7)
+     * pendingFollowUpAllAgainInfo - visitCount > 1 && sendstate in (1,2,3,7)
+     * pendingFollowUpWaitSendInfo - visitCount = 1 && sendstate in (1,2)
+     * pendingFollowUpWaitSendAgainInfo - visitCount > 1 && sendstate in (1,2)
+     * pendingFollowUpSentInfo - visitCount = 1 && sendstate in (3,7)
+     * pendingFollowUpSentAgainInfo - visitCount > 1 && sendstate in (3,7)
+     * sentInfo - visitCount = 1 && sendstate = 3
+     * sentAgainInfo - visitCount > 1 && sendstate = 3
+     * firstDischargeCountInfo - visitCount = 1
+     * againFollowUpPushCountInfo - visitCount = 2 && visitDeptCode = sys.smartor.center.dept
      * filterCountList - taskSituation = 6 && sendstate = 4
      *
      */
@@ -782,6 +798,9 @@
             log.error(logInfo);
             return error(logInfo);
         }
+        // 鍒嗙被缁熻涓烘眹鎬荤粨鏋滐紝涓嶅弬涓庡垎椤碉紱鍚屾椂閬垮厤 pageNum/pageSize 褰卞搷鍒嗙粍 key 鐨勬敹闆嗐��
+        serviceSubtaskCountReq.setPageNum(null);
+        serviceSubtaskCountReq.setPageSize(null);
         List<ServiceSubtaskStatistic> serviceSubtaskStatistics = serviceSubtaskService.getSfStatisticsScript(serviceSubtaskCountReq);
         return success(serviceSubtaskStatistics);
     }
@@ -817,10 +836,45 @@
         LoginUser loginUser = getLoginUser();
         String orgid = loginUser.getUser().getOrgid();
         serviceSubtaskCountReq.setOrgid(orgid);
-        PageUtils.startPageByPost(serviceSubtaskCountReq.getPageNum(), serviceSubtaskCountReq.getPageSize());
-        return success(serviceSubtaskService.getSfStatisticsScriptDetails(serviceSubtaskCountReq));
+        //娉ㄦ剰锛氳繖閲屼笉鑳藉啀璋冪敤 PageUtils.startPageByPost銆�
+        //杩斿洖缁撴瀯鏄� Map锛屼笖涓嶄紶 type 鏃朵細鎵ц涓ゆ潯 SQL锛孭ageHelper 鍙綔鐢ㄤ簬绗竴鏉� SQL锛�
+        //鍒嗛〉宸叉斁鍒� Service 閲屾寜棰樼洰锛坰criptid锛夊畬鎴愶紝淇濊瘉 total 鏄鐩�绘暟銆�
+        Map<String, Object> details = serviceSubtaskService.getSfStatisticsScriptDetails(serviceSubtaskCountReq);
+        AjaxResult ajax = success(details.get("data"));
+        //鍏煎鏃у墠绔細data 浠嶇劧鏄� {scriptid: 棰樼洰} 鐨� Map锛�
+        //鍚屾椂鎸� RuoYi 涔犳儻琛ヤ笂 rows/total/pageNum/pageSize 鏂逛究鍓嶇鍒嗛〉
+        ajax.put("rows", details.get("rows"));
+        ajax.put("total", details.get("total"));
+        ajax.put("pageNum", details.get("pageNum"));
+        ajax.put("pageSize", details.get("pageSize"));
+        return ajax;
     }
 
+    @ApiOperation("鑾峰彇闅忚鍒嗙被缁熻鏄庣粏璇︽儏瓒呴摼鎺�")
+    @AddOrgId(field = "orgid", paramIndex = 0, campusField = "campusid")
+    @PostMapping("/getSfStatisticsCountDetailsHyperLink")
+    public AjaxResult getSfStatisticsCountDetailsHyperLink(@RequestBody ServiceSubtaskCountReq serviceSubtaskCountReq) {
+        LoginUser loginUser = getLoginUser();
+        String orgid = loginUser.getUser().getOrgid();
+        String configKey = serviceSubtaskCountReq.getConfigKey();
+        if (StringUtils.isNotEmpty(configKey)) {
+            String configValue = sysConfigService.selectConfigByKey(configKey, orgid);
+            serviceSubtaskCountReq.setConfigValue(configValue);
+            if (StringUtils.isEmpty(configValue)) {
+                String logInfo = "getSfStatisticsCount-鍙傛暟 " + configKey + " 鏈厤缃�, 璇烽厤缃ソ鍚庨噸璇�";
+                log.error(logInfo);
+                return error(logInfo);
+            }
+        }
+
+        String followUpCountStyle = sysConfigService.selectConfigByKey("followUpCountStyle", orgid);
+        if (ObjectUtils.isNotEmpty(followUpCountStyle)) {
+            serviceSubtaskCountReq.setFollowUpCountStyle(followUpCountStyle);
+        } else {
+            serviceSubtaskCountReq.setFollowUpCountStyle("1");
+        }
+        return success(serviceSubtaskService.getSfStatisticsHyperlink(serviceSubtaskCountReq));
+    }
     /**
      * 鍙戦�佺煭淇℃ā鏉�
      */
@@ -838,48 +892,54 @@
     }
 
     /**
-     * 缁熻浠诲姟鍚勭鐘舵�佺殑鏁伴噺锛堜紭鍖栫増锛�
+     * 缁熻浠诲姟鍚勭鐘舵�佺殑鏁伴噺锛堜紭鍖栫増锛�-銆愰殢璁垮伐浣滃彴銆�
      *
+     * @param soonTimeoutHours 鍗冲皢瓒呮椂鏃堕棿锛堝皬鏃讹級锛岄粯璁� 48
      * @return Map<String, Object> 鍖呭惈鍚勭姸鎬佺粺璁℃暟鎹�
      * pendingVisitCount: 褰撳墠鐧诲綍浜烘埅姝粖鏃ュ緟闅忚鎬婚噺
      * failedVisitCount: 褰撳墠鐧诲綍浜洪殢璁夸换鍔″け璐ユ�婚噺
      * abnormalVisitVount: 褰撳墠鐧诲綍浜洪殢璁夸换鍔″紓甯告�婚噺
      * allVisitCount: 褰撳墠鐧诲綍浜洪殢璁夸换鍔℃�婚噺
+     * soonTimeoutCount: 褰撳墠鐧诲綍浜哄嵆灏嗚秴鏃舵暟閲忥紙瓒呮椂鎴鏃堕棿鍦� soonTimeoutHours 灏忔椂鍐咃級
      */
     @ApiOperation("鑾峰彇褰撳墠鐢ㄦ埛闅忚鏁伴噺缁熻")
     @PostMapping("/getCurrentUserServiceSubtaskCount")
-    public Map<String, Object> getCurrentUserServiceSubtaskCount() {
-        Map<String, Object> map = new HashMap<>();
-        LoginUser loginUser = getLoginUser();
+    public Map<String, Object> getCurrentUserServiceSubtaskCount(
+            @RequestParam(value = "soonTimeoutHours", required = false, defaultValue = "48") Integer soonTimeoutHours) {
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        if (loginUser == null || loginUser.getUser() == null) {
+            return new HashMap<>();
+        }
         SysUser sysUser = loginUser.getUser();
-        SysUserDept sysUserDept = new SysUserDept();
-        sysUserDept.setUserId(sysUser.getUserId());
-        sysUserDept.setOrgid(sysUser.getOrgid());
-        sysUserDept.setCampusid(sysUser.getCampusid());
-        sysUserDept.setDeptType("1");
-        List<SysUserDept> sysUserDepts = sysUserDeptService.selectSysUserDeptList(sysUserDept);
-        SysUserDept sysUserWard = new SysUserDept();
-        sysUserWard.setUserId(sysUser.getUserId());
-        sysUserWard.setOrgid(sysUser.getOrgid());
-        sysUserWard.setCampusid(sysUser.getCampusid());
-        sysUserWard.setDeptType("2");
-        List<SysUserDept> sysUserWards = sysUserDeptService.selectSysUserDeptList(sysUserWard);
-        List<String> deptCodes = sysUserDepts.stream().map(SysUserDept::getDeptCode).collect(Collectors.toList());
-        List<String> wardCodes = sysUserWards.stream().map(SysUserDept::getDeptCode).collect(Collectors.toList());
+        List<String> deptCodes = CollectionUtils.emptyIfNull(sysUser.getBelongDepts()).stream()
+                .map(item -> item.get("deptCode"))
+                .filter(Objects::nonNull) // 鍓旈櫎null
+                .map(Object::toString)
+                .collect(Collectors.toList());
+        List<String> wardCodes = CollectionUtils.emptyIfNull(sysUser.getBelongWards()).stream()
+                .map(item -> item.get("districtCode"))
+                .filter(Objects::nonNull) // 鍓旈櫎null
+                .map(Object::toString)
+                .collect(Collectors.toList());
         ServiceSubtaskEntity entity = new ServiceSubtaskEntity();
         entity.setOrgid(sysUser.getOrgid());
         entity.setLeaveldeptcodes(deptCodes);
         entity.setLeavehospitaldistrictcodes(wardCodes);
-        if (ObjectUtils.isNotEmpty(sysUser)) {
-            if (!("admin").equals(sysUser.getUserName())) {
-                if (ObjectUtils.isNotEmpty(sysUserWards) && ObjectUtils.isNotEmpty(sysUserDepts)) {
-                    map = serviceSubtaskService.getCurrentUserServiceSubtaskCount(entity);
-                }
-            } else {
-                map = serviceSubtaskService.getCurrentUserServiceSubtaskCount(entity);
-            }
+        entity.setSoonTimeoutHours(soonTimeoutHours);
+        // admin 鏌ョ湅鍏ㄩ櫌锛涢潪 admin 鍙閰嶇疆浜嗙瀹ゆ垨鐥呭尯浠讳竴缁村害鍗冲彲鏌ヨ锛圫QL 涓负 OR 鍏崇郴锛�
+        boolean isAdmin = "admin".equals(sysUser.getUserName());
+        if (isAdmin || CollectionUtils.isNotEmpty(deptCodes) || CollectionUtils.isNotEmpty(wardCodes)) {
+            return serviceSubtaskService.getCurrentUserServiceSubtaskCount(entity);
         }
-        return map;
+        // 闈� admin 涓旀湭閰嶇疆浠讳綍绉戝/鐥呭尯鏉冮檺鏃讹紝杩斿洖榛樿 0 鍊硷紝閬垮厤鍓嶇鍙栦笉鍒� key
+        Map<String, Object> empty = new HashMap<>();
+        empty.put("pendingVisitCount", 0);
+        empty.put("failedVisitCount", 0);
+        empty.put("abnormalVisitCount", 0);
+        empty.put("warnningVisitCount", 0);
+        empty.put("allVisitCount", 0);
+        empty.put("soonTimeoutCount", 0);
+        return empty;
     }
 
     /**
@@ -979,6 +1039,17 @@
     }
 
     /**
+     * 鎵嬪姩鍙戦�侀棶鍗锋ā鏉跨殑寰俊鍔熻兘
+     *
+     * @param subid
+     * @return
+     */
+    @GetMapping(value = "/weChatSubTask/{subid}")
+    public Map<String, Object> weChatSubTask(@PathVariable("subid") Long subid) {
+        return serviceSubtaskService.weChatSubTask(subid);
+    }
+
+    /**
      * 璺宠浆涓棿椤�
      *
      * @param taskId
diff --git a/ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtask.java b/ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtask.java
index c00bd91..5847cf4 100644
--- a/ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtask.java
+++ b/ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtask.java
@@ -179,11 +179,18 @@
     private String templatename;
 
     /**
-     * 鍙戝鐞嗘剰瑙侊細1鏆備笉澶勭悊銆�2閫氱煡灏辫瘖銆�3鐥呮儏绋冲畾銆�4浜哄伐闅忚銆�5澶辫閫乁UID
+     * 鍙戝鐞嗘剰瑙侊細1鏆備笉澶勭悊銆�2閫氱煡灏辫瘖銆�3鐥呮儏绋冲畾銆�4浜哄伐闅忚銆�5澶辫閫乁UID銆�6鍥為櫌澶嶈瘖澶嶆煡
      */
-    @Excel(name = " 澶勭悊鎰忚锛�1鏆備笉澶勭悊銆�2閫氱煡灏辫瘖銆�3鐥呮儏绋冲畾銆�4浜哄伐闅忚銆�5澶辫 ")
-    @ApiModelProperty(value = "澶勭悊鎰忚锛�1鏆備笉澶勭悊銆�2閫氱煡灏辫瘖銆�3鐥呮儏绋冲畾銆�4浜哄伐闅忚銆�5澶辫")
+    @Excel(name = " 澶勭悊鎰忚锛�1鏆備笉澶勭悊銆�2閫氱煡灏辫瘖銆�3鐥呮儏绋冲畾銆�4浜哄伐闅忚銆�5澶辫銆�6鍥為櫌澶嶈瘖澶嶆煡 ")
+    @ApiModelProperty(value = "澶勭悊鎰忚锛�1鏆備笉澶勭悊銆�2閫氱煡灏辫瘖銆�3鐥呮儏绋冲畾銆�4浜哄伐闅忚銆�5澶辫銆�6鍥為櫌澶嶈瘖澶嶆煡")
     private String suggest;
+
+    /**
+     * 鍖荤敓澶勭悊鎰忚
+     */
+    @Excel(name = "鍖荤敓澶勭悊鎰忚")
+    @ApiModelProperty(value = "鍖荤敓澶勭悊鎰忚")
+    private String doctorOpinion;
 
     /**
      * 鍙戦�佹棩鏈�
@@ -276,6 +283,12 @@
     @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
     @ApiModelProperty(value = "瀹屾垚鏃堕棿")
     private Date finishtime;
+
+    /**
+     * 瀹屾垚鏃剁姸鎬侊細1-姝e父瀹屾垚 2-寤舵湡瀹屾垚 3-瓒呮椂瀹屾垚
+     */
+    @ApiModelProperty(value = "瀹屾垚鏃剁姸鎬侊細1-姝e父瀹屾垚 2-寤舵湡瀹屾垚 3-瓒呮椂瀹屾垚")
+    private Long finishState;
 
     /**
      * 灏辫瘖璁板綍缂栧彿
@@ -754,4 +767,10 @@
     @Excel(name = " 浜哄伐鐢佃瘽鏍囪 ")
     @ApiModelProperty(value = "鏄惁浜哄伐鎵撹繃鐢佃瘽 0-娌℃墦杩� 1-鎵撹繃")
     private Long isManuallyCalled;
+
+    /**
+     * 鍐嶆闅忚鏂瑰紡锛堜粎鎺ュ彛璇锋眰鍙傛暟锛屼笉钀藉簱锛�
+     */
+    @ApiModelProperty(value = "鍐嶆闅忚鏂瑰紡 1-鏈瀹ら殢璁� 2-闅忚涓績闅忚锛堜粎璇锋眰鍙傛暟锛屼笉钀藉簱锛�")
+    private String resource;
 }
diff --git a/ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskCountReq.java b/ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskCountReq.java
index 1dc4a6a..ce4902a 100644
--- a/ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskCountReq.java
+++ b/ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskCountReq.java
@@ -238,6 +238,18 @@
     private String excep;
 
     /**
+     * 澶勭悊鎰忚缂栫爜 - 瓒呴摼鎺ョ粺璁�
+     */
+    @ApiModelProperty(value = "澶勭悊鎰忚缂栫爜")
+    private String suggest;
+
+    /**
+     * 瀹屾垚鏃剁姸鎬� - 瓒呴摼鎺ョ粺璁★紙1-姝e父瀹屾垚 2-寤舵湡瀹屾垚 3-瓒呮椂瀹屾垚锛�
+     */
+    @ApiModelProperty(value = "瀹屾垚鏃剁姸鎬侊細1姝e父瀹屾垚 2寤舵湡瀹屾垚 3瓒呮椂瀹屾垚")
+    private Long finishState;
+
+    /**
      * 褰撳墠鍙戦�佹柟寮� - 瓒呴摼鎺ョ粺璁�
      */
     @ApiModelProperty("褰撳墠鍙戦�佹柟寮�")
@@ -297,6 +309,12 @@
     private String hospType;
 
     /**
+     * 浜哄伐鐢佃瘽鏍囪
+     */
+    @ApiModelProperty(value = "鏄惁浜哄伐鎵撹繃鐢佃瘽 0-娌℃墦杩� 1-鎵撹繃")
+    private Long isManuallyCalled;
+
+    /**
      * pageNum
      */
     @ApiModelProperty("pageNum")
diff --git a/ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskExport.java b/ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskExport.java
index 34b6af9..40d5f28 100644
--- a/ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskExport.java
+++ b/ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskExport.java
@@ -200,5 +200,12 @@
     @ApiModelProperty(value = "澶囨敞")
     private String remark;
 
+    /**
+     * 鍖荤敓澶勭悊鎰忚
+     */
+    @Excel(name = "鍖荤敓澶勭悊鎰忚")
+    @ApiModelProperty(value = "鍖荤敓澶勭悊鎰忚")
+    private String doctorOpinion;
+
 
 }
diff --git a/ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskRes.java b/ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskRes.java
index d65bf63..f11c202 100644
--- a/ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskRes.java
+++ b/ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskRes.java
@@ -62,6 +62,12 @@
     private String remark;
 
     /**
+     * 鍖荤敓澶勭悊鎰忚
+     */
+    @ApiModelProperty(value = "鍖荤敓澶勭悊鎰忚")
+    private String doctorOpinion;
+
+    /**
      * 澶囨敞
      */
     @ApiModelProperty(value = "")
@@ -186,10 +192,10 @@
     private String templatename;
 
     /**
-     * 鍙戝鐞嗘剰瑙侊細1鏆備笉澶勭悊銆�2閫氱煡灏辫瘖銆�3鐥呮儏绋冲畾銆�4浜哄伐闅忚銆�5澶辫閫乁UID
+     * 鍙戝鐞嗘剰瑙侊細1鏆備笉澶勭悊銆�2閫氱煡灏辫瘖銆�3鐥呮儏绋冲畾銆�4浜哄伐闅忚銆�5澶辫閫乁UID銆�6鍥為櫌澶嶈瘖澶嶆煡
      */
-    @Excel(name = " 澶勭悊鎰忚锛�1鏆備笉澶勭悊銆�2閫氱煡灏辫瘖銆�3鐥呮儏绋冲畾銆�4浜哄伐闅忚銆�5澶辫 ")
-    @ApiModelProperty(value = "澶勭悊鎰忚锛�1鏆備笉澶勭悊銆�2閫氱煡灏辫瘖銆�3鐥呮儏绋冲畾銆�4浜哄伐闅忚銆�5澶辫")
+    @Excel(name = " 澶勭悊鎰忚锛�1鏆備笉澶勭悊銆�2閫氱煡灏辫瘖銆�3鐥呮儏绋冲畾銆�4浜哄伐闅忚銆�5澶辫銆�6鍥為櫌澶嶈瘖澶嶆煡 ")
+    @ApiModelProperty(value = "澶勭悊鎰忚锛�1鏆備笉澶勭悊銆�2閫氱煡灏辫瘖銆�3鐥呮儏绋冲畾銆�4浜哄伐闅忚銆�5澶辫銆�6鍥為櫌澶嶈瘖澶嶆煡")
     private String suggest;
 
     /**
@@ -293,6 +299,12 @@
     private Date finishtime;
 
     /**
+     * 瀹屾垚鏃剁姸鎬侊細1-姝e父瀹屾垚 2-寤舵湡瀹屾垚 3-瓒呮椂瀹屾垚
+     */
+    @ApiModelProperty(value = "瀹屾垚鏃剁姸鎬侊細1-姝e父瀹屾垚 2-寤舵湡瀹屾垚 3-瓒呮椂瀹屾垚")
+    private Long finishState;
+
+    /**
      * 灏辫瘖璁板綍缂栧彿
      */
     @Excel(name = " 灏辫瘖璁板綍缂栧彿 ")
diff --git a/ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskStatistic.java b/ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskStatistic.java
index d840716..43fc52d 100644
--- a/ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskStatistic.java
+++ b/ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskStatistic.java
@@ -103,6 +103,14 @@
     @Excel(name = " 鍑洪櫌浜烘 ")
     private Integer dischargeCount = 0;
 
+    @ApiModelProperty(value = "棣栨鍑洪櫌浜烘锛坴isitCount = 1锛�")
+    @Excel(name = " 棣栨鍑洪櫌浜烘 ")
+    private Long firstDischargeCount = 0L;
+
+    @ApiModelProperty(value = "鍐嶆闅忚鎺ㄩ�佷汉鏁帮紙棣栦釜鍐嶆闅忚涓旀墽琛岀瀹や负闅忚涓績锛夊啀娆¢殢璁挎帹閫佺巼锛堝啀娆¢殢璁挎帹閫佷汉鏁� / 棣栨鍑洪櫌浜烘锛�")
+    @Excel(name = " 鍐嶆闅忚鎺ㄩ�佷汉鏁� ")
+    private Long againFollowUpPushCount = 0L;
+
     @ApiModelProperty(value = "鏃犻渶闅忚浜烘")
     @Excel(name = " 鏃犻渶闅忚浜烘 ")
     private Long nonFollowUp = 0L;
@@ -114,6 +122,10 @@
     @ApiModelProperty(value = "搴旈殢璁夸汉娆�")
     @Excel(name = " 搴旈殢璁夸汉娆� ")
     private Long followUpNeeded = 0L;
+
+    @ApiModelProperty(value = "浜哄伐宸叉嫧閫氭�婚噺")
+    @Excel(name = " 浜哄伐宸叉嫧閫氭�婚噺 ")
+    private Long manuallyCalled = 0L;
 
     @ApiModelProperty(value = "棣栨鏃犻渶闅忚")
     private Long nonFollowUpFirst = 0L;
@@ -133,6 +145,10 @@
     @ApiModelProperty(value = "棣栨闅忚澶辫触")
     @Excel(name = " 棣栨闅忚澶辫触 ")
     private Long followUpFail = 0L;
+
+    @ApiModelProperty(value = "鍙婃椂闅忚浜烘锛堝凡瀹屾垚涓旀湭瓒呮椂锛宖inish_state=1锛�")
+    @Excel(name = " 鍙婃椂闅忚浜烘 ")
+    private Long timelyFollowUp = 0L;
 
     @ApiModelProperty(value = "鍑洪櫌浜烘璇︽儏")
     private List<ServiceSubtask> dischargeCountInfo;
@@ -154,6 +170,9 @@
 
     @ApiModelProperty(value = "棣栨闅忚澶辫触璇︽儏")
     private List<ServiceSubtask> followUpFailInfo;
+
+    @ApiModelProperty(value = "鍙婃椂闅忚璇︽儏")
+    private List<ServiceSubtask> timelyFollowUpInfo;
 
     @ApiModelProperty(value = "棣栨闅忚浜哄伐璇︽儏")
     private List<ServiceSubtask> manualInfo;
@@ -268,6 +287,46 @@
     @Excel(name = " 浜屾寰呴殢璁� ")
     private Long pendingFollowUpAgain = 0L;
 
+    @ApiModelProperty(value = "棣栨闅忚浜哄伐鐢佃瘽瓒呮椂锛坰endstate=7 涓� isManuallyCalled=0锛�")
+    @Excel(name = " 棣栨闅忚浜哄伐鐢佃瘽瓒呮椂 ")
+    private Long manualCallTimeout = 0L;
+
+    @ApiModelProperty(value = "鍐嶆闅忚浜哄伐鐢佃瘽瓒呮椂锛坰endstate=7 涓� isManuallyCalled=0锛�")
+    @Excel(name = " 鍐嶆闅忚浜哄伐鐢佃瘽瓒呮椂 ")
+    private Long manualCallTimeoutAgain = 0L;
+
+    @ApiModelProperty(value = "棣栨寰呴殢璁垮叏閮紙sendstate in 1,2,3,7锛�")
+    @Excel(name = " 棣栨寰呴殢璁垮叏閮� ")
+    private Long pendingFollowUpAll = 0L;
+
+    @ApiModelProperty(value = "鍐嶆寰呴殢璁垮叏閮紙sendstate in 1,2,3,7锛�")
+    @Excel(name = " 鍐嶆寰呴殢璁垮叏閮� ")
+    private Long pendingFollowUpAllAgain = 0L;
+
+    @ApiModelProperty(value = "棣栨寰呴殢璁垮緟鍙戦�侊紙sendstate in 1,2锛�")
+    @Excel(name = " 棣栨寰呴殢璁垮緟鍙戦�� ")
+    private Long pendingFollowUpWaitSend = 0L;
+
+    @ApiModelProperty(value = "鍐嶆寰呴殢璁垮緟鍙戦�侊紙sendstate in 1,2锛�")
+    @Excel(name = " 鍐嶆寰呴殢璁垮緟鍙戦�� ")
+    private Long pendingFollowUpWaitSendAgain = 0L;
+
+    @ApiModelProperty(value = "棣栨寰呴殢璁垮凡鍙戦�侊紙sendstate in 3,7锛�")
+    @Excel(name = " 棣栨寰呴殢璁垮凡鍙戦�� ")
+    private Long pendingFollowUpSent = 0L;
+
+    @ApiModelProperty(value = "鍐嶆寰呴殢璁垮凡鍙戦�侊紙sendstate in 3,7锛�")
+    @Excel(name = " 鍐嶆寰呴殢璁垮凡鍙戦�� ")
+    private Long pendingFollowUpSentAgain = 0L;
+
+    @ApiModelProperty(value = "棣栨宸插彂閫侊紙sendstate=3锛�")
+    @Excel(name = " 棣栨宸插彂閫� ")
+    private Long sent = 0L;
+
+    @ApiModelProperty(value = "鍐嶆宸插彂閫侊紙sendstate=3锛�")
+    @Excel(name = " 鍐嶆宸插彂閫� ")
+    private Long sentAgain = 0L;
+
     @ApiModelProperty(value = "浜屾闅忚鎴愬姛")
     @Excel(name = " 浜屾闅忚鎴愬姛 ")
     private Long followUpSuccessAgain = 0L;
@@ -276,6 +335,10 @@
     @Excel(name = " 浜屾闅忚澶辫触 ")
     private Long followUpFailAgain = 0L;
 
+    @ApiModelProperty(value = "鍐嶆闅忚鍥為櫌澶嶈瘖澶嶆煡锛坰uggest=6锛�")
+    @Excel(name = " 鍐嶆闅忚鍥為櫌澶嶈瘖澶嶆煡 ")
+    private Long suggest6CountAgain = 0L;
+
     @ApiModelProperty(value = "浜屾闅忚鐜�")
     @Excel(name = " 浜屾闅忚鐜� ")
     private String followUpRateAgain;
diff --git a/ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceTask.java b/ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceTask.java
index 724794b..57cc36c 100644
--- a/ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceTask.java
+++ b/ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceTask.java
@@ -117,6 +117,13 @@
     private Long sendDay;
 
     /**
+     * 寤舵湡瀹屾垚澶╂暟锛堥殢璁夸换鍔″畬鎴愭椂鐢ㄤ簬鍒ゆ柇鏄惁瓒呮椂锛岀己鐪�0锛�
+     */
+    @Excel(name = "寤舵湡瀹屾垚澶╂暟")
+    @ApiModelProperty(value = "寤舵湡瀹屾垚澶╂暟")
+    private Long delayFinishDays;
+
+    /**
      * 鎮h�呭鍚�
      */
     @Excel(name = "鎮h�呭鍚�")
diff --git a/ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceTaskVO.java b/ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceTaskVO.java
index 7c2ec31..b2a7d01 100644
--- a/ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceTaskVO.java
+++ b/ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceTaskVO.java
@@ -51,6 +51,13 @@
     private Long sendDay;
 
     /**
+     * 寤舵湡瀹屾垚澶╂暟锛堥殢璁夸换鍔″畬鎴愭椂鐢ㄤ簬鍒ゆ柇鏄惁瓒呮椂锛岀己鐪�0锛�
+     */
+    @Excel(name = "寤舵湡瀹屾垚澶╂暟")
+    @ApiModelProperty(value = "寤舵湡瀹屾垚澶╂暟")
+    private Long delayFinishDays;
+
+    /**
      *
      */
     @Excel(name = "  ")
diff --git a/ruoyi-task/src/main/java/com/ruoyi/task/domain/VO/ServiceSubtaskVO.java b/ruoyi-task/src/main/java/com/ruoyi/task/domain/VO/ServiceSubtaskVO.java
index 840f7e3..7620f5f 100644
--- a/ruoyi-task/src/main/java/com/ruoyi/task/domain/VO/ServiceSubtaskVO.java
+++ b/ruoyi-task/src/main/java/com/ruoyi/task/domain/VO/ServiceSubtaskVO.java
@@ -79,10 +79,10 @@
     private String senduuid;
 
     /**
-     * 鍙戝鐞嗘剰瑙侊細1鏆備笉澶勭悊銆�2閫氱煡灏辫瘖銆�3鐥呮儏绋冲畾銆�4浜哄伐闅忚銆�5澶辫閫乁UID
+     * 鍙戝鐞嗘剰瑙侊細1鏆備笉澶勭悊銆�2閫氱煡灏辫瘖銆�3鐥呮儏绋冲畾銆�4浜哄伐闅忚銆�5澶辫閫乁UID銆�6鍥為櫌澶嶈瘖澶嶆煡
      */
-    @Excel(name = " 澶勭悊鎰忚锛�1鏆備笉澶勭悊銆�2閫氱煡灏辫瘖銆�3鐥呮儏绋冲畾銆�4浜哄伐闅忚銆�5澶辫 ")
-    @ApiModelProperty(value = "澶勭悊鎰忚锛�1鏆備笉澶勭悊銆�2閫氱煡灏辫瘖銆�3鐥呮儏绋冲畾銆�4浜哄伐闅忚銆�5澶辫")
+    @Excel(name = " 澶勭悊鎰忚锛�1鏆備笉澶勭悊銆�2閫氱煡灏辫瘖銆�3鐥呮儏绋冲畾銆�4浜哄伐闅忚銆�5澶辫銆�6鍥為櫌澶嶈瘖澶嶆煡 ")
+    @ApiModelProperty(value = "澶勭悊鎰忚锛�1鏆備笉澶勭悊銆�2閫氱煡灏辫瘖銆�3鐥呮儏绋冲畾銆�4浜哄伐闅忚銆�5澶辫銆�6鍥為櫌澶嶈瘖澶嶆煡")
     private String suggest;
 
     /**
@@ -476,6 +476,12 @@
     @ApiModelProperty(value = "褰撳墠鍙戦�佹柟寮�")
     private String currentPreachform;
 
+    /**
+     * 褰撳墠鎵ц鏂瑰紡澶氶�夌瓫閫夛紝澶氫釜鍊肩敤閫楀彿鍒嗛殧
+     */
+    @ApiModelProperty(value = "褰撳墠鎵ц鏂瑰紡澶氶�夌瓫閫夛紝澶氫釜鍊肩敤閫楀彿鍒嗛殧")
+    private String currentPreachforms;
+
 
     /**
      * 搴婂彿
@@ -651,6 +657,12 @@
     @ApiModelProperty(value = "澶囨敞")
     private String remark;
 
+    /**
+     * 鍖荤敓澶勭悊鎰忚
+     */
+    @ApiModelProperty(value = "鍖荤敓澶勭悊鎰忚")
+    private String doctorOpinion;
+
 
     /**
      * 鍑洪櫌鏃堕棿
@@ -819,6 +831,18 @@
     @ApiModelProperty(value = "椤甸潰灞曠ず鐘舵�� 锛�1锛氬緟闅忚锛�2 寰呭彂閫侊級锛�2锛氶殢璁夸腑锛�3 宸插彂閫併��1 琚鍙栵級銆�3锛氭湭瀹屾垚锛�5 鍙戦�佸け璐ャ��7銆佽秴鏃讹級銆�4锛氬凡瀹屾垚锛� 6 宸插畬鎴愶級銆�5锛氭棤闇�闅忚锛�4 涓嶆墽琛岋級")
     private Long sendstateView;
 
+    /**
+     * 鏄惁鍗冲皢瓒呮椂锛�1 鏄紙閰嶅悎 soonTimeoutHours 浣跨敤锛岀敤浜庢煡璇㈠嵆灏嗚秴鏃跺垪琛級
+     */
+    @ApiModelProperty(value = "鏄惁鍗冲皢瓒呮椂:1鏄�")
+    private Integer soonTimeout;
+
+    /**
+     * 鍗冲皢瓒呮椂鏃堕棿锛堝皬鏃讹級锛氭埅姝㈡椂闂村湪 N 灏忔椂鍐�
+     */
+    @ApiModelProperty(value = "鍗冲皢瓒呮椂鏃堕棿(灏忔椂)")
+    private Integer soonTimeoutHours;
+
 //    /**
 //     * 鏃ユ湡闄愬埗 鐩墠鐢卞墠绔帶鍒�
 //     */
diff --git a/ruoyi-task/src/main/java/com/ruoyi/task/domain/entity/ServiceSubtaskEntity.java b/ruoyi-task/src/main/java/com/ruoyi/task/domain/entity/ServiceSubtaskEntity.java
index 881db8f..a71dadc 100644
--- a/ruoyi-task/src/main/java/com/ruoyi/task/domain/entity/ServiceSubtaskEntity.java
+++ b/ruoyi-task/src/main/java/com/ruoyi/task/domain/entity/ServiceSubtaskEntity.java
@@ -85,10 +85,10 @@
     private String senduuid;
 
     /**
-     * 鍙戝鐞嗘剰瑙侊細1鏆備笉澶勭悊銆�2閫氱煡灏辫瘖銆�3鐥呮儏绋冲畾銆�4浜哄伐闅忚銆�5澶辫閫乁UID
+     * 鍙戝鐞嗘剰瑙侊細1鏆備笉澶勭悊銆�2閫氱煡灏辫瘖銆�3鐥呮儏绋冲畾銆�4浜哄伐闅忚銆�5澶辫閫乁UID銆�6鍥為櫌澶嶈瘖澶嶆煡
      */
-    @Excel(name = " 澶勭悊鎰忚锛�1鏆備笉澶勭悊銆�2閫氱煡灏辫瘖銆�3鐥呮儏绋冲畾銆�4浜哄伐闅忚銆�5澶辫 ")
-    @ApiModelProperty(value = "澶勭悊鎰忚锛�1鏆備笉澶勭悊銆�2閫氱煡灏辫瘖銆�3鐥呮儏绋冲畾銆�4浜哄伐闅忚銆�5澶辫")
+    @Excel(name = " 澶勭悊鎰忚锛�1鏆備笉澶勭悊銆�2閫氱煡灏辫瘖銆�3鐥呮儏绋冲畾銆�4浜哄伐闅忚銆�5澶辫銆�6鍥為櫌澶嶈瘖澶嶆煡 ")
+    @ApiModelProperty(value = "澶勭悊鎰忚锛�1鏆備笉澶勭悊銆�2閫氱煡灏辫瘖銆�3鐥呮儏绋冲畾銆�4浜哄伐闅忚銆�5澶辫銆�6鍥為櫌澶嶈瘖澶嶆煡")
     private String suggest;
 
     /**
@@ -113,6 +113,18 @@
      */
     @ApiModelProperty(value = "鍑犲ぉ鍙婃椂鐜�")
     private Integer rateDay;
+
+    /**
+     * 鏄惁鍗冲皢瓒呮椂锛�1 鏄紙閰嶅悎 soonTimeoutHours 浣跨敤锛�
+     */
+    @ApiModelProperty(value = "鏄惁鍗冲皢瓒呮椂:1鏄�")
+    private Integer soonTimeout;
+
+    /**
+     * 鍗冲皢瓒呮椂鏃堕棿锛堝皬鏃讹級锛氱粺璁¤窛瓒呮椂鎴鏃堕棿鍦� N 灏忔椂鍐呯殑寰呴殢璁挎暟閲�
+     */
+    @ApiModelProperty(value = "鍗冲皢瓒呮椂鏃堕棿(灏忔椂)")
+    private Integer soonTimeoutHours;
 
     /**
      * 鎬у埆
@@ -484,6 +496,12 @@
     @ApiModelProperty(value = "褰撳墠鍙戦�佹柟寮�")
     private String currentPreachform;
 
+    /**
+     * 褰撳墠鎵ц鏂瑰紡澶氶�夌瓫閫夛紝澶氫釜鍊肩敤閫楀彿鍒嗛殧
+     */
+    @ApiModelProperty(value = "褰撳墠鎵ц鏂瑰紡澶氶�夌瓫閫夛紝澶氫釜鍊肩敤閫楀彿鍒嗛殧")
+    private String currentPreachforms;
+
 
     /**
      * 搴婂彿
@@ -659,6 +677,12 @@
     @ApiModelProperty(value = "澶囨敞")
     private String remark;
 
+    /**
+     * 鍖荤敓澶勭悊鎰忚
+     */
+    @ApiModelProperty(value = "鍖荤敓澶勭悊鎰忚")
+    private String doctorOpinion;
+
 
     /**
      * 鍑洪櫌鏃堕棿
diff --git a/ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceSubtaskMapper.java b/ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceSubtaskMapper.java
index f669994..b954c08 100644
--- a/ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceSubtaskMapper.java
+++ b/ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceSubtaskMapper.java
@@ -38,6 +38,9 @@
 
     /**
      * 鏌ヨ鍗曚竴浠诲姟锛堥殢璁匡級鍒楄〃
+     * <p>
+     * 鍏ュ弬 soonTimeout=1 涓� soonTimeoutHours=N 鏃讹紝浠呰繑鍥炩�滃皻鏈秴鏃躲�佷笖灏嗗湪 N 灏忔椂鍐呰秴鏃垛�濈殑鏁版嵁锛�
+     * 鎴鏃堕棿鍙e緞涓� getCurrentUserServiceSubtaskCount 鐨� soonTimeoutCount 涓�鑷淬��
      *
      * @param serviceSubtaskVO 鍗曚竴浠诲姟锛堥殢璁匡級
      * @return 鍗曚竴浠诲姟锛堥殢璁匡級闆嗗悎
@@ -168,6 +171,30 @@
     public int deleteServiceSubtaskByCondition(@Param("taskid") Long id, @Param("patid") Long patid);
 
     /**
+     * 鍒犻櫎鎮h�呭嚭闄㈣褰曞搴旂殑鏈畬鎴愰殢璁夸换鍔�
+     *
+     * @param patid      鎮h�匢D
+     * @param inhospids  鍑洪櫌璁板綍ID闆嗗悎
+     * @param sendstates 鏈畬鎴愮姸鎬侀泦鍚�
+     * @return 鍒犻櫎鏉℃暟
+     */
+    public int deleteUnfinishedServiceSubtaskByInhospids(@Param("patid") Long patid,
+                                                         @Param("inhospids") List<Long> inhospids,
+                                                         @Param("sendstates") List<Long> sendstates);
+
+    /**
+     * 缁熻鎮h�呮寚瀹氫换鍔′笅鏈畬鎴愮殑闅忚浠诲姟鏁伴噺锛堢敤浜庝汉宸ラ殢璁块噸澶嶆牎楠岋級
+     *
+     * @param patid      鎮h�匢D
+     * @param taskid     浠诲姟ID
+     * @param sendstates 鏈畬鎴愮姸鎬侀泦鍚�
+     * @return 鏈畬鎴愪换鍔℃暟閲�
+     */
+    int countUnfinishedServiceSubtaskByPatidAndTaskid(@Param("patid") Long patid,
+                                                      @Param("taskid") Long taskid,
+                                                      @Param("sendstates") List<Long> sendstates);
+
+    /**
      * 鍒犻櫎鍗曚竴浠诲姟锛堥殢璁匡級
      *
      * @return 缁撴灉
@@ -246,6 +273,7 @@
      * ywc: 宸插畬鎴愭暟閲� sendstate = 6L
      * dfs: 寰呭彂閫佹暟閲� sendstate = 1L, 3L, 4L, 5L, 6L, 7L
      * yfs: 宸插彂閫佹暟閲� sendstate = 2L
+     * cs: 瓒呮椂鏁伴噺 sendstate = 7L
      * yc: 寮傚父鏁伴噺 excep = '1'
      * yc: 璀﹀憡鏁伴噺 excep = '2'
      */
@@ -260,6 +288,7 @@
      * failedVisitCount: 褰撳墠鐧诲綍浜洪殢璁夸换鍔″け璐ユ�婚噺
      * abnormalVisitVount: 褰撳墠鐧诲綍浜洪殢璁夸换鍔″紓甯告�婚噺
      * allVisitCount: 褰撳墠鐧诲綍浜洪殢璁夸换鍔℃�婚噺
+     * soonTimeoutCount: 褰撳墠鐧诲綍浜哄嵆灏嗚秴鏃舵暟閲忥紙瓒呮椂鎴鏃堕棿鍦� soonTimeoutHours 灏忔椂鍐咃紝浠呯粺璁″皻鏈秴鏃剁殑鏁版嵁锛�
      */
     public Map<String, Object> getCurrentUserServiceSubtaskCount(ServiceSubtaskEntity serviceSubtaskVO);
 
@@ -327,4 +356,14 @@
 
     
 
+    /**
+     * 鏉′欢鏇存柊鍘熶换鍔$殑鈥滄槸鍚﹀啀娆¢殢璁库�濇爣璇嗭紝鐢ㄤ簬浜屾闅忚骞傜瓑鎺у埗銆�
+     * 浠呭綋褰撳墠鍊间笉鏄洰鏍囧�兼椂鎵嶆洿鏂帮紝杩斿洖鍙楀奖鍝嶈鏁般��
+     *
+     * @param id           瀛愪换鍔� ID
+     * @param isVisitAgain 鐩爣鍊硷紙2-宸插啀娆¢殢璁匡級
+     * @return 鍙楀奖鍝嶈鏁帮紝1 琛ㄧず鎶㈠埌鐢熸垚鏉冿紝0 琛ㄧず宸茶鍏朵粬璇锋眰澶勭悊
+     */
+    int updateIsVisitAgainById(@Param("id") Long id, @Param("isVisitAgain") Integer isVisitAgain);
+
 }
diff --git a/ruoyi-task/src/main/java/com/ruoyi/task/service/IServiceSubtaskService.java b/ruoyi-task/src/main/java/com/ruoyi/task/service/IServiceSubtaskService.java
index 6150ab1..046def5 100644
--- a/ruoyi-task/src/main/java/com/ruoyi/task/service/IServiceSubtaskService.java
+++ b/ruoyi-task/src/main/java/com/ruoyi/task/service/IServiceSubtaskService.java
@@ -99,6 +99,16 @@
     public String addSubTaskAgain(ServiceSubtask ServiceSubtask);
 
     /**
+     * 棣栨闅忚浠诲姟瓒呮椂鍚庯紝鎸変笅涓�浠诲姟鑷姩鐢熸垚鍐嶆闅忚浠诲姟锛堥殢璁夸腑蹇冿級銆�
+     * <p>
+     * 涓庢墜鍔ㄥ叆鍙g殑宸紓锛氶殢璁夸腑蹇冪瀹ゆ湭閰嶇疆鏃跺洖閫�鍘熶换鍔$瀹わ紱鍚屼竴鍘熶换鍔″彧鐢熸垚涓�鏉★紙閿� + 鏉′欢鏇存柊骞傜瓑锛夈��
+     *
+     * @param serviceSubtask 宸茶秴鏃剁殑棣栨闅忚瀛愪换鍔�
+     * @return 缁撴灉鎻忚堪
+     */
+    public String addSubTaskAgainByDeadline(ServiceSubtask serviceSubtask);
+
+    /**
      * 淇敼鍗曚竴浠诲姟锛堥殢璁匡級
      *
      * @param ServiceSubtask 鍗曚竴浠诲姟锛堥殢璁匡級
@@ -171,12 +181,19 @@
 
     public List<ServiceSubtaskStatistic> getSfStatisticsScript(ServiceSubtaskCountReq serviceSubtaskCountReq);
 
-    public Map<String, ServiceSubtaskScriptCount> getSfStatisticsScriptDetails(ServiceSubtaskCountReq serviceSubtaskCountReq);
+    public Map<String, Object> getSfStatisticsScriptDetails(ServiceSubtaskCountReq serviceSubtaskCountReq);
 
     public List<PatArchiveSrmVO> sendMsgTemplate(List<PatArchiveSrmVO> patArchiveSrmVOS);
 
     public List<ServiceSubtaskDiagname> convertToDiagnameList(List<ServiceSubtask> subtaskList);
 
+    /**
+     * 缁熻浠诲姟鍚勭鐘舵�佺殑鏁伴噺 -銆愰殢璁垮伐浣滃彴銆�
+     *
+     * @param entity 鏌ヨ鏉′欢锛堢瀹�/鐥呭尯鏉冮檺銆乻oonTimeoutHours 鍗冲皢瓒呮椂灏忔椂鏁扮瓑锛�
+     * @return 鍚� pendingVisitCount銆乫ailedVisitCount銆乤bnormalVisitCount銆亀arnningVisitCount銆�
+     * allVisitCount銆乻oonTimeoutCount 鐨勭粺璁$粨鏋�
+     */
     public Map<String, Object> getCurrentUserServiceSubtaskCount(ServiceSubtaskEntity entity);
 
     /**
@@ -216,7 +233,13 @@
 
     Map<String, Object> smsSubTask(Long subid);
 
-    
+    /**
+     * 涓芥按涓尰闄㈠井淇″叕浼楀彿鍙戦�侊細master 鏂板鐨勫澶栧叆鍙o紙瀹炵幇绫诲悓鍚嶆柟娉曪級銆�
+     * <p>
+     * 涓� {@link #smsSubTask(Long)} 鐨勫尯鍒細鐭俊璇疯蛋 ruoyi-message 鐨� SmsSenderFactory锛�
+     * 璇ュ叆鍙e彧鍋氫附姘村井淇$壒鏈夌殑閾捐矾锛屼緵闇�瑕佺洿鍙戝叕浼楀彿鐨勮皟鐢ㄦ柟浣跨敤銆�
+     */
+    Map<String, Object> weChatSubTask(Long subid);
 
     
 
diff --git a/ruoyi-task/src/main/java/com/ruoyi/task/service/IServiceTaskdeptService.java b/ruoyi-task/src/main/java/com/ruoyi/task/service/IServiceTaskdeptService.java
index 02885e9..9134876 100644
--- a/ruoyi-task/src/main/java/com/ruoyi/task/service/IServiceTaskdeptService.java
+++ b/ruoyi-task/src/main/java/com/ruoyi/task/service/IServiceTaskdeptService.java
@@ -67,4 +67,12 @@
      * @return 缁撴灉
      */
     public int deleteServiceTaskdeptById(Long id);
+
+    /**
+     * 鎸変换鍔D鍒犻櫎绉戝鍏宠仈锛屽彲鎸囧畾绉戝绫诲瀷
+     *
+     * @param serviceTaskdept 妯℃澘绉戝鍏宠仈
+     * @return 缁撴灉
+     */
+    public int deleteServiceTaskdeptByTaskId(ServiceTaskdept serviceTaskdept);
 }
diff --git a/ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceSubtaskServiceImpl.java b/ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceSubtaskServiceImpl.java
index 8548af1..901c761 100644
--- a/ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceSubtaskServiceImpl.java
+++ b/ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceSubtaskServiceImpl.java
@@ -42,6 +42,7 @@
 import java.time.format.DateTimeFormatter;
 import java.time.temporal.ChronoUnit;
 import java.util.*;
+import java.util.concurrent.TimeUnit;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
@@ -122,7 +123,9 @@
 import com.ruoyi.task.domain.ServiceTaskVO;
 import com.ruoyi.task.domain.VO.TransferDeptVO;
 import com.ruoyi.system.service.ISysConfigService;
+import com.ruoyi.system.service.ISysDeptService;
 import com.ruoyi.system.mapper.ext.SysDept2Mapper;
+import com.ruoyi.task.api.contract.OperationSubtaskWriter;
 
 /**
  * 鍗曚竴浠诲姟锛堥殢璁匡級Service涓氬姟灞傚鐞�
@@ -133,6 +136,15 @@
 @Slf4j
 @Service
 public class ServiceSubtaskServiceImpl implements IServiceSubtaskService {
+
+    /**
+     * 闅忚瀛愪换鍔$粍瑁呭櫒锛堝師 smartor 渚� IPatMedInhospService#boxedServiceSubtask 鍦ㄦ湰妯″潡鐨勮惤鍦板疄鐜帮級銆�
+     * <p>
+     * 浜屾闅忚瑕佺敤銆屼笅涓�浠诲姟閰嶇疆 + 鍑洪櫌璁板綍 + 鎮h�呮。妗堛�嶇粍瑁呮柊鐨� service_subtask锛�
+     * 杩欐鍙e緞涓庨暱鏈熶换鍔$敓鎴愬畬鍏ㄤ竴鑷达紝鍥犳鐩存帴澶嶇敤鏈ā鍧楀凡鏈夌殑缁勮鍣紝涓嶅啀澶嶅埗涓�浠姐��
+     */
+    @Autowired
+    private OperationSubtaskWriter operationSubtaskWriter;
 
     /**
      * 婊℃剰搴︽ā鍧楀澶栭棬闈紙甯搁┗ Bean锛夈��
@@ -218,6 +230,9 @@
     private TaskArchivePort taskArchivePort;
 
     @Autowired
+    private ISysDeptService sysDeptService;
+
+    @Autowired
     private ScheduleSubtaskBuilder scheduleSubtaskBuilder;
 
 
@@ -260,6 +275,10 @@
     private ServiceOutPathMapper serviceOutPathMapper;
 
     private static final String KEY_SEP = "\u0001";
+
+    /** 浜屾闅忚鐢熸垚閿佸墠缂�锛岄槻姝㈠悓涓�鍘熶换鍔″苟鍙戠敓鎴愬鏉′簩娆¢殢璁� */
+    private static final String AGAIN_LOCK_KEY_PREFIX = "smartor:again:lock:";
+    private static final long AGAIN_LOCK_SECONDS = 30L;
 
     /**
      * 鏌ヨ鍗曚竴浠诲姟锛堥殢璁匡級
@@ -485,7 +504,7 @@
             if (StringUtils.isEmpty(serviceSubtask.getCurrentPreachform())) {
                 if (StringUtils.isNotEmpty(serviceSubtask.getPreachform())) {
                     //榛樿鍙栫涓�涓�
-                    serviceSubtaskRes.setCurrentPreachform(serviceSubtask.getPreachform().split(",")[0]);
+                    serviceSubtaskRes.setCurrentPreachform(serviceSubtask.getPreachform().split(",")[0].trim());
                 }
             }
             PatMedInhosp inhosp = loadInhospCached(inhospCache, serviceSubtask.getInhospid());
@@ -691,11 +710,22 @@
 
     @Override
     public List<ServiceSubtask> patItem2(ServiceSubtaskEntity serviceSubtaskEntity) {
+        if (serviceSubtaskEntity.getSendstateView() != null) {
+            //            1锛氬緟闅忚锛�1 琚鍙栥��2 寰呭彂閫併��3 宸插彂閫併��5 鍙戦�佸け璐ャ��7銆佽秴鏃讹級锛�2锛氬凡瀹屾垚锛� 6 宸插畬鎴愶級銆�3锛氭棤闇�闅忚锛�4 涓嶆墽琛岋級
+            if (serviceSubtaskEntity.getSendstateView() == 1)
+                serviceSubtaskEntity.setSendstates(new ArrayList<>(Arrays.asList(1L, 2L, 3L, 5L, 7L)));
+            if (serviceSubtaskEntity.getSendstateView() == 2)
+                serviceSubtaskEntity.setSendstates(new ArrayList<>(Arrays.asList(6L)));
+            if (serviceSubtaskEntity.getSendstateView() == 3)
+                serviceSubtaskEntity.setSendstates(new ArrayList<>(Arrays.asList(4L)));
+        }
         List<ServiceSubtask> serviceSubtaskList = new ArrayList<>();
-        for (Long sendstate : serviceSubtaskEntity.getSendstates()) {
-            serviceSubtaskEntity.setSendstate(sendstate);
-            List<ServiceSubtask> serviceSubtasks = serviceSubtaskMapper.selectServiceSubtaskList(serviceSubtaskEntity);
-            serviceSubtaskList.addAll(serviceSubtasks);
+        if(CollectionUtils.isNotEmpty(serviceSubtaskEntity.getSendstates())){
+            for (Long sendstate : serviceSubtaskEntity.getSendstates()) {
+                serviceSubtaskEntity.setSendstate(sendstate);
+                List<ServiceSubtask> serviceSubtasks = serviceSubtaskMapper.selectServiceSubtaskList(serviceSubtaskEntity);
+                serviceSubtaskList.addAll(serviceSubtasks);
+            }
         }
         return serviceSubtaskList;
     }
@@ -765,6 +795,7 @@
             result.putIfAbsent("xsf", 0);
             result.putIfAbsent("dsf", 0);
             result.putIfAbsent("ywc", 0);
+            result.putIfAbsent("cs", 0);
             result.putIfAbsent("yc", 0);
             result.putIfAbsent("jg", 0);
         }
@@ -801,6 +832,7 @@
             result.putIfAbsent("dsf", 0);
             result.putIfAbsent("dfs", 0);
             result.putIfAbsent("yfs", 0);
+            result.putIfAbsent("cs", 0);
             result.putIfAbsent("ywc", 0);
             result.putIfAbsent("yc", 0);
             result.putIfAbsent("jg", 0);
@@ -947,6 +979,7 @@
      * @return 缁撴灉
      */
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public String addSubTaskAgain(ServiceSubtask serviceSubtask) {
         ServiceSubtask serviceSubtask1 = selectServiceSubtaskById(serviceSubtask.getId());
         PatMedInhosp patMedInhosp = new PatMedInhosp();
@@ -956,7 +989,7 @@
         PatArchive patArchive = taskArchivePort.findByPatid(serviceSubtask1.getPatid());
         //鏅畞 鏂板鍐嶆闅忚涓嶈兘瑙﹀彂鎮h�呭啀鍏ラ櫌
         boolean inhospAgain = true;
-        if (serviceSubtask1.getOrgid().equals("47255004333112711A1001")) {
+        if ("47255004333112711A1001".equals(serviceSubtask1.getOrgid())) {
             inhospAgain = false;
         }
         if (patMedInhospList.size() > 0 && inhospAgain) {
@@ -970,13 +1003,17 @@
             serviceSubtask1.setRemark(StringUtils.isNotEmpty(serviceSubtask.getRemark()) ? serviceSubtask.getRemark() + ";鏃犻渶鍐嶆闅忚锛屼换鍔¤鏍囪涓嶅湪鎵ц" : "鏃犻渶鍐嶆闅忚锛屼换鍔¤鏍囪涓嶅湪鎵ц");
             serviceSubtaskMapper.updateServiceSubtask(serviceSubtask1);
             return "鏃犻渶鍐嶆闅忚锛屼换鍔¤鏍囪涓嶅湪鎵ц";
-        } else if (StringUtils.isNotEmpty(patArchive.getNotrequiredFlag()) && patArchive.getNotrequiredFlag().equals("1")) {
+        } else if (patArchive != null && StringUtils.isNotEmpty(patArchive.getNotrequiredFlag()) && patArchive.getNotrequiredFlag().equals("1")) {
             serviceSubtask1.setIsVisitAgain(2);
             serviceSubtaskMapper.updateServiceSubtask(serviceSubtask1);
             serviceSubtask1.setRemark(StringUtils.isNotEmpty(serviceSubtask.getRemark()) ? serviceSubtask.getRemark() + ";鏃犻渶鍐嶆闅忚锛屾偅鑰呰鏍囪涓嶉渶瑕佽鏈嶅姟" : "鏃犻渶鍐嶆闅忚锛屾偅鑰呰鏍囪涓嶉渶瑕佽鏈嶅姟");
             return "鏃犻渶鍐嶆闅忚锛屾偅鑰呰鏍囪涓嶉渶瑕佽鏈嶅姟";
         } else if (ObjectUtils.isNotEmpty(serviceSubtask1.getIsVisitAgain()) && serviceSubtask1.getIsVisitAgain() == 2) {
             return "鏃犻渶鍐嶆闅忚锛岃浠诲姟宸茬粡鍐嶆闅忚杩囦簡";
+        }
+        //涓芥按涓尰闄㈡牴鎹笅娆′换鍔D鐢熸垚鍐嶆闅忚
+        if ("47231022633110211A2101".equals(serviceSubtask1.getOrgid())) {
+            return addSubTaskAgainNextTaskId(serviceSubtask1, serviceSubtask);
         }
         //鏂板鍐嶆闅忚浠诲姟
         ServiceSubtask serviceSubtask2 = DtoConversionUtils.sourceToTarget(serviceSubtask, ServiceSubtask.class);
@@ -1004,6 +1041,482 @@
             return "鍐嶆闅忚浠诲姟鍒涘缓鎴愬姛";
         }
         return "鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屽啀璇锋閲嶈瘯";
+    }
+
+    public String addSubTaskAgainNextTaskId(ServiceSubtask serviceSubtask, ServiceSubtask request) {
+        if (ObjectUtils.isEmpty(serviceSubtask.getTaskid())) {
+            log.error("鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛岀己灏慣askId, 鍐嶈娆¢噸璇�");
+            return "鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛岀己灏慣askId, 鍐嶈娆¢噸璇�";
+        }
+        // 鍚屼竴鍘熶换鍔′覆琛岀敓鎴愶紝闃叉骞跺彂閲嶅鎻掑叆
+        String lockToken = tryLockAgainTask(serviceSubtask.getId());
+        if (lockToken == null) {
+            return "鍐嶆闅忚浠诲姟姝e湪鍒涘缓涓紝璇峰嬁閲嶅鎻愪氦";
+        }
+        try {
+            ServiceTask serviceTask = serviceTaskService.selectServiceTaskByTaskid(serviceSubtask.getTaskid());
+            if (ObjectUtils.isEmpty(serviceTask)) {
+                log.error("鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屾壘涓嶅埌瀵瑰簲浠诲姟 taskid = {}, 鍐嶈娆¢噸璇�", serviceSubtask.getTaskid());
+                return "鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屾壘涓嶅埌瀵瑰簲浠诲姟 taskid = " + serviceSubtask.getTaskid() + ", 鍐嶈娆¢噸璇�";
+            }
+            // nexttaskflag=0 琛ㄧず浠诲姟閾剧粓姝紝涓嶅厑璁稿啀鐢熸垚
+            if ("0".equals(serviceTask.getNexttaskflag())) {
+                log.info("鏃犻渶鍐嶆闅忚锛屼换鍔″凡鏍囪鏃犲悗缁殢璁�, taskid = {}", serviceSubtask.getTaskid());
+                return "鏃犻渶鍐嶆闅忚锛岃浠诲姟鏃犲悗缁殢璁垮畨鎺�";
+            }
+            // 骞傜瓑鏍¢獙锛氬悓涓�鍘熶换鍔″彧鍏佽鐢熸垚涓�鏉′簩娆¢殢璁�
+            ServiceSubtaskEntity againQuery = new ServiceSubtaskEntity();
+            againQuery.setUpid(serviceSubtask.getId().intValue());
+            if (CollectionUtils.isNotEmpty(serviceSubtaskMapper.selectServiceSubtaskList(againQuery))) {
+                return "鏃犻渶鍐嶆闅忚锛岃浠诲姟宸茬粡鍐嶆闅忚杩囦簡";
+            }
+            // 鏌ヨ鍑洪櫌璁板綍
+            PatMedInhosp patMedInhospVo = new PatMedInhosp();
+            patMedInhospVo.setOrgid(serviceSubtask.getOrgid());
+            patMedInhospVo.setInhospid(serviceSubtask.getInhospid());
+            patMedInhospVo.setCry(1);
+            List<PatMedInhosp> patMedInhospList = inhospVisitFacade.selectPatMedInhospList(patMedInhospVo);
+            PatMedInhosp patMedInhosp = new PatMedInhosp();
+            if (CollectionUtils.isNotEmpty(patMedInhospList)) {
+                patMedInhosp = patMedInhospList.get(0);
+            } else {
+                log.error("鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屼换鍔$己灏� 瀵瑰簲鐨勫嚭闄㈣褰�, taskid = {}, 鍐嶈娆¢噸璇�", serviceSubtask.getTaskid());
+                return "鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屼换鍔$己灏� 瀵瑰簲鐨勫嚭闄㈣褰�, taskid = " + serviceSubtask.getTaskid() + ", 鍐嶈娆¢噸璇�";
+            }
+            // 鏈厤缃� nexttaskid 鏃跺鐢ㄥ綋鍓嶄换鍔¢厤缃紝闃叉鍘嗗彶浠诲姟鐩存帴澶辫触
+            Long nexttaskid = serviceTask.getNexttaskid();
+            if (ObjectUtils.isEmpty(nexttaskid) || Long.valueOf(0L).equals(nexttaskid)) {
+                nexttaskid = serviceTask.getTaskid();
+                log.warn("鍐嶆闅忚浠诲姟鏈厤缃� nexttaskid锛屾寜褰撳墠浠诲姟閰嶇疆鐢熸垚, taskid = {}", nexttaskid);
+            }
+            ServiceTask nextServiceTask = serviceTaskService.selectServiceTaskByTaskid(nexttaskid);
+            if (ObjectUtils.isEmpty(nextServiceTask)) {
+                log.error("鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屾壘涓嶅埌瀵瑰簲浠诲姟 nexttaskid = {}, 鍐嶈娆¢噸璇�", nexttaskid);
+                return "鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屾壘涓嶅埌瀵瑰簲浠诲姟 nexttaskid = " + nexttaskid + ", 鍐嶈娆¢噸璇�";
+            }
+            PatArchive patArchive = taskArchivePort.findByPatid(serviceSubtask.getPatid());
+            if (ObjectUtils.isEmpty(patArchive)) {
+                log.error("鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屾湭鎵惧埌鎮h�呮。妗� patid = {}, subTaskId = {}", serviceSubtask.getPatid(), serviceSubtask.getId());
+                return "鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屾湭鎵惧埌鎮h�呮。妗�, subTaskId = " + serviceSubtask.getId() + ", 鍐嶈娆¢噸璇�";
+            }
+            String config = sysConfigService.selectConfigByKey("visit.early.day");
+            Integer type = 1;
+            String resource = resolveAgainResource(request);
+            if (StringUtils.isEmpty(resource)) {
+                String requestResource = request == null ? null : request.getResource();
+                log.error("鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屽啀娆¢殢璁挎柟寮忎笉鍚堟硶, resource = {}, subTaskId = {}", requestResource, serviceSubtask.getId());
+                return "鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屽啀娆¢殢璁挎柟寮忎笉鍚堟硶, resource = " + requestResource + ", 鍐嶈娆¢噸璇�";
+            }
+            String visitDeptCode;
+            String visitDeptName;
+            if ("1".equals(resource)) {
+                // 鏈瀹ら殢璁匡細璇锋眰绉戝浼樺厛锛屾湭浼犳椂浣跨敤鍘熶换鍔″嚭闄㈢瀹わ紱涓嶄緷璧栧師浠诲姟 visitType
+                String requestDeptCode = firstNonBlank(request == null ? null : request.getVisitDeptCode());
+                String requestDeptName = firstNonBlank(request == null ? null : request.getVisitDeptName());
+                if (StringUtils.isNotEmpty(requestDeptCode)) {
+                    visitDeptCode = requestDeptCode;
+                    visitDeptName = requestDeptName;
+                } else {
+                    // 鍑洪櫌绉戝锛歴ervice_subtask 閲屽搴旂殑鏄� leaveldeptcode锛坉eptcode 宸叉敼涓哄湪闄㈢瀹わ級
+                    visitDeptCode = serviceSubtask.getLeaveldeptcode();
+                    visitDeptName = serviceSubtask.getLeaveldeptname();
+                }
+                if (StringUtils.isEmpty(visitDeptCode)) {
+                    log.error("鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屾湰绉戝闅忚鏈壘鍒伴殢璁跨瀹�, subTaskId = {}", serviceSubtask.getId());
+                    return "鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屾湰绉戝闅忚鏈壘鍒伴殢璁跨瀹�, subTaskId = " + serviceSubtask.getId() + ", 鍐嶈娆¢噸璇�";
+                }
+                SysDept ownDept = selectDeptByCode(serviceSubtask.getOrgid(), visitDeptCode);
+                if (ObjectUtils.isNotEmpty(ownDept)) {
+                    visitDeptCode = ownDept.getDeptCode();
+                    visitDeptName = ownDept.getDeptName();
+                } else if (StringUtils.isNotEmpty(requestDeptCode) || StringUtils.isEmpty(visitDeptName)) {
+                    log.error("鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屾湰绉戝闅忚绉戝涓嶅瓨鍦�, deptCode = {}, subTaskId = {}", visitDeptCode, serviceSubtask.getId());
+                    return "鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屾湰绉戝闅忚绉戝涓嶅瓨鍦�, deptCode = " + visitDeptCode + ", 鍐嶈娆¢噸璇�";
+                }
+            } else {
+                // 闅忚涓績绉戝闅忚
+                String centerDept = sysConfigService.selectConfigByKey("sys.smartor.center.dept", serviceSubtask.getOrgid());
+                if (ObjectUtils.isEmpty(centerDept)) {
+                    log.error("鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屾湭閰嶇疆闅忚涓績绉戝鍙傛暟 sys.smartor.center.dept ,subTaskId = {}, 鍐嶈娆¢噸璇�", serviceSubtask.getId());
+                    return "鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屾湭閰嶇疆闅忚涓績绉戝鍙傛暟 sys.smartor.center.dept ,subTaskId = " + serviceSubtask.getId() + ", 鍐嶈娆¢噸璇�";
+                }
+                SysDept sysDeptVo = new SysDept();
+                sysDeptVo.setDeptType("1");
+                sysDeptVo.setOrgid(serviceSubtask.getOrgid());
+                sysDeptVo.setDeptCode(centerDept);
+                List<SysDept> sysDepts = sysDeptService.selectDeptList(sysDeptVo);
+                SysDept sysDept = new SysDept();
+                if (CollectionUtils.isNotEmpty(sysDepts)) {
+                    sysDept = sysDepts.get(0);
+                } else {
+                    log.error("鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屾湭鎵惧埌鍙傛暟 sys.smartor.center.dept 閰嶇疆鐨勯殢璁夸腑蹇冪瀹� deptCode = {}, 鍐嶈娆¢噸璇�", centerDept);
+                    return "鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屾湭鎵惧埌鍙傛暟 sys.smartor.center.dept 閰嶇疆鐨勯殢璁夸腑蹇冪瀹� deptCode = " + centerDept + ", 鍐嶈娆¢噸璇�";
+                }
+                visitDeptCode = sysDept.getDeptCode();
+                visitDeptName = sysDept.getDeptName();
+            }
+
+            // 鎸変笅涓�浠诲姟鐢熸垚浜屾闅忚瀛愪换鍔★紙琛ㄥ崟銆佸彂閫佹柟寮忓彇鑷笅涓�浠诲姟锛夛紱
+            // 缁勮閫昏緫宸插湪鏈ā鍧楃殑 OperationSubtaskWriter 閲岋紙鍘� IPatMedInhospService#boxedServiceSubtask锛夛紝杩欓噷鐩存帴澶嶇敤
+            ServiceSubtask serviceSubtaskAgain = operationSubtaskWriter.boxedServiceSubtask(nextServiceTask,
+                    patMedInhosp, patArchive, config, type);
+            if (ObjectUtils.isEmpty(serviceSubtaskAgain) || ObjectUtils.isEmpty(serviceSubtaskAgain.getTaskid())) {
+                log.error("鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屼笅涓�浠诲姟鐢熸垚瀛愪换鍔′负绌�, subTaskId = {}, nextTaskId = {}", serviceSubtask.getId(), nexttaskid);
+                return "鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屾偅鑰呭嚭闄㈣褰曚笉瀹屾暣鎴栦笅涓�浠诲姟閰嶇疆寮傚父, 鍐嶈娆¢噸璇�";
+            }
+            Date now = new Date();
+            serviceSubtaskAgain.setId(null);
+            serviceSubtaskAgain.setUpid(serviceSubtask.getId().intValue());
+            serviceSubtaskAgain.setVisitCount(ObjectUtils.isNotEmpty(serviceSubtask.getVisitCount())
+                    ? serviceSubtask.getVisitCount() + 1 : 2);
+            serviceSubtaskAgain.setSendstate(2L);
+            serviceSubtaskAgain.setIsVisitAgain(1);
+            serviceSubtaskAgain.setCreateTime(now);
+            serviceSubtaskAgain.setUpdateTime(now);
+
+            // 浜屾闅忚鏃堕棿锛氫笂娆¢殢璁垮畬鎴愭椂闂达紙鏈畬鎴愬垯褰撳墠鏃堕棿锛�+ 涓嬩竴浠诲姟鍙戦�佸ぉ鏁帮紱杩囧幓鏃堕棿鍏滃簳涓哄綋鍓嶆椂闂�
+            applyAgainVisitTime(serviceSubtaskAgain, serviceSubtask, nextServiceTask, config, now);
+            // 鏂颁换鍔$殑闅忚鏂瑰紡璁板綍鏈閫夋嫨鐨勫啀娆¢殢璁挎柟寮忥紱resource 鏈韩涓嶈惤搴�
+            serviceSubtaskAgain.setVisitType(resource);
+            serviceSubtaskAgain.setVisitDeptCode(visitDeptCode);
+            serviceSubtaskAgain.setVisitDeptName(visitDeptName);
+            int i = serviceSubtaskMapper.insertServiceSubtask(serviceSubtaskAgain);
+            if (i != 1) {
+                return "鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屽啀璇锋閲嶈瘯";
+            }
+            // 鍒濆鍖栧彂閫佹柟寮忔槑缁嗭紝琛ュ伩锛坈ompensateTask锛夊拰瓒呮椂鍒ゆ柇渚濊禆璇ヨ〃
+            addServiceSubtaskPreachform(serviceSubtaskAgain, null);
+            // 鏉′欢鏇存柊鍘熶换鍔★細0 琛岃鏄庡凡琚叾浠栬姹傜敓鎴愶紝鎶涘紓甯稿洖婊氭湰娆℃彃鍏ョ殑閲嶅鏁版嵁
+            int updated = serviceSubtaskMapper.updateIsVisitAgainById(serviceSubtask.getId(), 2);
+            if (updated != 1) {
+                throw new BaseException("鏃犻渶鍐嶆闅忚锛岃浠诲姟宸茬粡鍐嶆闅忚杩囦簡");
+            }
+            return "鍐嶆闅忚浠诲姟鍒涘缓鎴愬姛";
+        } finally {
+            unlockAgainTask(serviceSubtask.getId(), lockToken);
+        }
+    }
+
+    /**
+     * 瑙f瀽鍐嶆闅忚鏂瑰紡锛氫粎鍙栬姹傚弬鏁� resource锛屼笉浣跨敤鍘熶换鍔� visitType锛涙湭浼犳椂榛樿闅忚涓績銆�
+     *
+     * @return 1-鏈瀹ら殢璁匡紱2-闅忚涓績闅忚锛沶ull-闈炴硶鍊�
+     */
+    private String resolveAgainResource(ServiceSubtask request) {
+        String resource = request == null ? null : request.getResource();
+        if (StringUtils.isEmpty(resource) || StringUtils.isEmpty(resource.trim())) {
+            return "2";
+        }
+        resource = resource.trim();
+        return ("1".equals(resource) || "2".equals(resource)) ? resource : null;
+    }
+
+    private String firstNonBlank(String... values) {
+        if (values == null) {
+            return null;
+        }
+        for (String value : values) {
+            if (StringUtils.isNotEmpty(value) && StringUtils.isNotEmpty(value.trim())) {
+                return value.trim();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 鎸� orgid + deptCode 鏌ヨ鍚敤绉戝锛屾煡涓嶅埌杩斿洖 null銆�
+     */
+    private SysDept selectDeptByCode(String orgid, String deptCode) {
+        if (StringUtils.isEmpty(orgid) || StringUtils.isEmpty(deptCode)) {
+            return null;
+        }
+        SysDept query = new SysDept();
+        query.setDeptType("1");
+        query.setOrgid(orgid);
+        query.setDeptCode(deptCode);
+        List<SysDept> deptList = sysDeptService.selectDeptList(query);
+        return CollectionUtils.isEmpty(deptList) ? null : deptList.get(0);
+    }
+
+    /**
+     * 浜屾闅忚鏃堕棿锛氶娆¢殢璁垮畬鎴愭椂闂达紙鏈畬鎴愬彇褰撳墠鏃堕棿锛�+ 涓嬩竴浠诲姟鍙戦�佸ぉ鏁帮紝杩囧幓鏃堕棿鍏滃簳涓哄綋鍓嶆椂闂淬��
+     * 閫氱煡绫讳换鍔℃寜 visit.early.day 鎻愬墠鍙戦�併��
+     */
+    private void applyAgainVisitTime(ServiceSubtask target, ServiceSubtask origin, ServiceTask nextTask,
+                                     String earlyDayConfig, Date now) {
+        if (target == null || origin == null || nextTask == null || now == null) {
+            return;
+        }
+        Date baseTime = ObjectUtils.isNotEmpty(origin.getFinishtime()) ? origin.getFinishtime() : now;
+        int intervalDays = ObjectUtils.isNotEmpty(nextTask.getSendDay()) ? nextTask.getSendDay().intValue() : 0;
+        Date dueTime = addDays(baseTime, intervalDays);
+        if (dueTime.before(now)) {
+            dueTime = now;
+        }
+        target.setLongSendTime(dueTime);
+        Date visitTime = dueTime;
+        if ("3".equals(nextTask.getType())) {
+            visitTime = addDays(dueTime, -parseIntConfig(earlyDayConfig));
+            if (visitTime.before(now)) {
+                visitTime = now;
+            }
+        }
+        target.setVisitTime(visitTime);
+    }
+
+    private int parseIntConfig(String value) {
+        if (StringUtils.isEmpty(value)) {
+            return 0;
+        }
+        try {
+            return Integer.parseInt(value.trim());
+        } catch (NumberFormatException e) {
+            log.warn("閰嶇疆鍊间笉鏄悎娉曟暣鏁帮紝鎸�0澶勭悊, value={}", value);
+            return 0;
+        }
+    }
+
+    /**
+     * 鏃ユ湡鍔犲噺澶╂暟锛坉ays 鍙负璐燂級銆�
+     * <p>
+     * 璇存槑锛氭湰妯″潡鍏跺畠鍦版柟鐢ㄧ殑鏄� {@code DateUtils.addDays}锛屼絾 ruoyi-common 鐨� DateUtils 骞舵湭鎻愪緵璇ユ柟娉曪紝
+     * 杩欓噷鏄簩娆¢殢璁挎椂闂磋绠楄嚜甯︾殑绉佹湁瀹炵幇锛岄伩鍏嶄緷璧栦笉瀛樺湪鐨勫伐鍏锋柟娉曘��
+     */
+    private Date addDays(Date date, int days) {
+        if (date == null) {
+            return null;
+        }
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(date);
+        calendar.add(Calendar.DAY_OF_MONTH, days);
+        return calendar.getTime();
+    }
+
+    /**
+     * 棣栨闅忚浠诲姟瓒呮椂鍚庯紝鎸変笅涓�浠诲姟鑷姩鐢熸垚鍐嶆闅忚浠诲姟銆�
+     * <p>
+     * 瀹氭椂浠诲姟鍏ュ彛 {@code RyTask#dealSubtaskDeadline} 浣跨敤銆備笌鎵嬪姩鍏ュ彛涓�鑷寸殑閮ㄥ垎锛�
+     * 琛ㄥ崟/鍙戦�佹柟寮忓彇涓嬩竴浠诲姟銆侀摼璺� upid + visit_count銆佸彂閫佹柟寮忔槑缁嗗垵濮嬪寲锛�
+     * 宸紓锛氳秴鏃跺啀娆¢殢璁垮浐瀹氳蛋闅忚涓績绉戝锛堥殢璁夸腑蹇冩湭閰嶇疆鏃跺洖閫�鍘熶换鍔$瀹わ級锛屽師浠诲姟缃负鍙戦�佸け璐ワ紙sendstate=5锛夈��
+     * <p>
+     * 骞傜瓑锛氬悓涓�鍘熶换鍔″彧鍏佽鐢熸垚涓�鏉″啀娆¢殢璁匡紙Redis 閿� + upid 鏍¢獙 + 鍘熶换鍔℃潯浠舵洿鏂帮級锛�
+     * 澶氳妭鐐�/澶氱嚎绋嬪悓鏃舵壂鎻忔椂锛屽悗鍒扮殑浜嬪姟浼氬湪鏉′欢鏇存柊澶勬嬁鍒� 0 琛屽苟鍥炴粴鏈鎻掑叆銆�
+     *
+     * @param serviceSubtask 宸茶秴鏃剁殑棣栨闅忚瀛愪换鍔�
+     * @return 缁撴灉鎻忚堪
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public String addSubTaskAgainByDeadline(ServiceSubtask serviceSubtask) {
+        if (ObjectUtils.isEmpty(serviceSubtask) || ObjectUtils.isEmpty(serviceSubtask.getId())) {
+            return "鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屽師浠诲姟涓嶅瓨鍦�";
+        }
+        String lockToken = tryLockAgainTask(serviceSubtask.getId());
+        if (lockToken == null) {
+            return "鍐嶆闅忚浠诲姟姝e湪鍒涘缓涓紝璇峰嬁閲嶅鎻愪氦";
+        }
+        try {
+            // 骞傜瓑鏍¢獙锛氬悓涓�鍘熶换鍔″彧鍏佽鐢熸垚涓�鏉″啀娆¢殢璁�
+            ServiceSubtaskEntity againQuery = new ServiceSubtaskEntity();
+            againQuery.setUpid(serviceSubtask.getId().intValue());
+            if (CollectionUtils.isNotEmpty(serviceSubtaskMapper.selectServiceSubtaskList(againQuery))) {
+                return "鏃犻渶鍐嶆闅忚锛岃浠诲姟宸茬粡鍐嶆闅忚杩囦簡";
+            }
+            // 鍘熶换鍔″凡琚爣璁颁负涓嶅啀闅忚锛堟墜鍔ㄥ叆鍙e啓杩� is_visit_again=2锛�
+            if (ObjectUtils.isNotEmpty(serviceSubtask.getIsVisitAgain()) && serviceSubtask.getIsVisitAgain() == 2) {
+                return "鏃犻渶鍐嶆闅忚锛岃浠诲姟宸茬粡鍐嶆闅忚杩囦簡";
+            }
+            ServiceTask serviceTask = serviceTaskService.selectServiceTaskByTaskid(serviceSubtask.getTaskid());
+            if (ObjectUtils.isEmpty(serviceTask)) {
+                return "鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屾壘涓嶅埌瀵瑰簲浠诲姟 taskid = " + serviceSubtask.getTaskid();
+            }
+            if ("0".equals(serviceTask.getNexttaskflag())) {
+                return "鏃犻渶鍐嶆闅忚锛岃浠诲姟鏃犲悗缁殢璁垮畨鎺�";
+            }
+            if (ObjectUtils.isEmpty(serviceTask.getNexttaskid())) {
+                return "鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屼换鍔$己灏� nexttaskid, taskid = " + serviceSubtask.getTaskid();
+            }
+            ServiceTask nextServiceTask = serviceTaskService.selectServiceTaskByTaskid(serviceTask.getNexttaskid());
+            if (ObjectUtils.isEmpty(nextServiceTask)) {
+                return "鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屾壘涓嶅埌瀵瑰簲浠诲姟 nexttaskid = " + serviceTask.getNexttaskid();
+            }
+            PatMedInhosp patMedInhosp = selectOutHospRecord(serviceSubtask);
+            if (ObjectUtils.isEmpty(patMedInhosp)) {
+                return "鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屼换鍔$己灏戝搴旂殑鍑洪櫌璁板綍, taskid = " + serviceSubtask.getTaskid();
+            }
+            PatArchive patArchive = taskArchivePort.findByPatid(serviceSubtask.getPatid());
+            if (ObjectUtils.isEmpty(patArchive)) {
+                return "鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屾湭鎵惧埌鎮h�呮。妗�, subTaskId = " + serviceSubtask.getId();
+            }
+            String config = sysConfigService.selectConfigByKey("visit.early.day");
+            String visitDeptCode;
+            String visitDeptName;
+            // 闅忚涓績绉戝锛氭湭閰嶇疆锛堟垨绉戝涓嶅瓨鍦級鏃跺洖閫�鍘熶换鍔$瀹わ紝淇濇寔瓒呮椂鍏ュ彛鍘熸湁琛屼负
+            SysDept centerDept = selectCenterDept(serviceSubtask.getOrgid());
+            if (ObjectUtils.isNotEmpty(centerDept)) {
+                visitDeptCode = centerDept.getDeptCode();
+                visitDeptName = centerDept.getDeptName();
+            } else {
+                String fallbackDeptCode = firstNonBlank(serviceSubtask.getVisitDeptCode());
+                if (StringUtils.isNotEmpty(fallbackDeptCode)) {
+                    visitDeptCode = fallbackDeptCode;
+                    visitDeptName = serviceSubtask.getVisitDeptName();
+                } else {
+                    // 鍑洪櫌绉戝锛歴ervice_subtask 閲屽搴旂殑鏄� leaveldeptcode锛坉eptcode 宸叉敼涓哄湪闄㈢瀹わ級
+                    visitDeptCode = serviceSubtask.getLeaveldeptcode();
+                    visitDeptName = serviceSubtask.getLeaveldeptname();
+                }
+            }
+            // 缁勮閫昏緫澶嶇敤鏈ā鍧� OperationSubtaskWriter锛堝師 IPatMedInhospService#boxedServiceSubtask锛�
+            ServiceSubtask serviceSubtaskAgain = operationSubtaskWriter.boxedServiceSubtask(nextServiceTask,
+                    patMedInhosp, patArchive, config, 1);
+            if (ObjectUtils.isEmpty(serviceSubtaskAgain) || ObjectUtils.isEmpty(serviceSubtaskAgain.getTaskid())) {
+                return "鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屾偅鑰呭嚭闄㈣褰曚笉瀹屾暣鎴栦笅涓�浠诲姟閰嶇疆寮傚父, subTaskId = " + serviceSubtask.getId();
+            }
+            Date now = new Date();
+            serviceSubtaskAgain.setId(null);
+            serviceSubtaskAgain.setUpid(serviceSubtask.getId().intValue());
+            serviceSubtaskAgain.setVisitCount(ObjectUtils.isNotEmpty(serviceSubtask.getVisitCount())
+                    ? serviceSubtask.getVisitCount() + 1 : 2);
+            serviceSubtaskAgain.setSendstate(2L);
+            serviceSubtaskAgain.setIsVisitAgain(1);
+            serviceSubtaskAgain.setCreateTime(now);
+            serviceSubtaskAgain.setUpdateTime(now);
+            // 瓒呮椂鍐嶆闅忚鍥哄畾涓洪殢璁夸腑蹇冮殢璁�
+            serviceSubtaskAgain.setVisitType("2");
+            serviceSubtaskAgain.setVisitDeptCode(visitDeptCode);
+            serviceSubtaskAgain.setVisitDeptName(visitDeptName);
+            if (serviceSubtaskMapper.insertServiceSubtask(serviceSubtaskAgain) != 1) {
+                return "鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屽啀璇锋閲嶈瘯";
+            }
+            // 鍒濆鍖栧彂閫佹柟寮忔槑缁嗭紝琛ュ伩锛坈ompensateTask锛夊拰瓒呮椂鍒ゆ柇渚濊禆璇ヨ〃
+            addServiceSubtaskPreachform(serviceSubtaskAgain, null);
+            // 鏉′欢鏇存柊鍘熶换鍔★細0 琛岃鏄庡凡琚叾浠栬姹傜敓鎴愶紝鎶涘紓甯稿洖婊氭湰娆℃彃鍏ョ殑閲嶅鏁版嵁
+            if (serviceSubtaskMapper.updateIsVisitAgainById(serviceSubtask.getId(), 2) != 1) {
+                throw new BaseException("鏃犻渶鍐嶆闅忚锛岃浠诲姟宸茬粡鍐嶆闅忚杩囦簡");
+            }
+            // 鍘熶换鍔¤秴鏃讹紝缃负鍙戦�佸け璐�
+            ServiceSubtask originUpdate = new ServiceSubtask();
+            originUpdate.setId(serviceSubtask.getId());
+            originUpdate.setSendstate(5L);
+            originUpdate.setUpdateTime(now);
+            serviceSubtaskMapper.updateServiceSubtask(originUpdate);
+            return "鍐嶆闅忚浠诲姟鍒涘缓鎴愬姛";
+        } finally {
+            unlockAgainTask(serviceSubtask.getId(), lockToken);
+        }
+    }
+
+    /**
+     * 鎸� orgid + inhospid 鍙栧嚭闄㈣褰曪紙瓒呮椂鍐嶆闅忚鐢級銆�
+     */
+    private PatMedInhosp selectOutHospRecord(ServiceSubtask serviceSubtask) {
+        PatMedInhosp patMedInhospVo = new PatMedInhosp();
+        patMedInhospVo.setOrgid(serviceSubtask.getOrgid());
+        patMedInhospVo.setInhospid(serviceSubtask.getInhospid());
+        patMedInhospVo.setCry(1);
+        List<PatMedInhosp> patMedInhospList = inhospVisitFacade.selectPatMedInhospList(patMedInhospVo);
+        if (CollectionUtils.isEmpty(patMedInhospList)) {
+            return null;
+        }
+        return patMedInhospList.get(0);
+    }
+
+    /**
+     * 鍙栭殢璁夸腑蹇冪瀹わ紝鏈厤缃垨绉戝涓嶅瓨鍦ㄦ椂杩斿洖 null锛堢敱璋冪敤鏂瑰洖閫�鍘熶换鍔$瀹わ級銆�
+     */
+    private SysDept selectCenterDept(String orgid) {
+        String centerDept = sysConfigService.selectConfigByKey("sys.smartor.center.dept", orgid);
+        if (StringUtils.isEmpty(centerDept)) {
+            return null;
+        }
+        SysDept sysDeptVo = new SysDept();
+        sysDeptVo.setDeptType("1");
+        sysDeptVo.setOrgid(orgid);
+        sysDeptVo.setDeptCode(centerDept);
+        // 瀹氭椂浠诲姟绾跨▼娌℃湁鐧诲綍涓婁笅鏂囷紝涓嶈兘璋冪敤甯� @DataScope 鐨� selectDeptList锛屽惁鍒欎細鎶涒�滆幏鍙栫敤鎴蜂俊鎭紓甯糕�濄��
+        List<SysDept> sysDepts = sysDeptService.selectSysDeptList(sysDeptVo);
+        return CollectionUtils.isEmpty(sysDepts) ? null : sysDepts.get(0);
+    }
+
+    /**
+     * 鑾峰彇浜屾闅忚鐢熸垚閿併�俁edis 寮傚父鏃堕檷绾т负鏁版嵁搴撳箓绛夋牎楠岋紝涓嶉樆鏂笟鍔°��
+     */
+    private String tryLockAgainTask(Long subId) {
+        String token = UUID.randomUUID().toString();
+        try {
+            Boolean locked = redisCache.setCacheObjectIfAbsent(
+                    AGAIN_LOCK_KEY_PREFIX + subId, token, AGAIN_LOCK_SECONDS, TimeUnit.SECONDS);
+            return Boolean.TRUE.equals(locked) ? token : null;
+        } catch (Exception e) {
+            log.warn("鍐嶆闅忚鑾峰彇Redis閿佸け璐ワ紝闄嶇骇浣跨敤鏁版嵁搴撳箓绛夋牎楠�, subId={}", subId, e);
+            return token;
+        }
+    }
+
+    /**
+     * 閲婃斁浜屾闅忚鐢熸垚閿侊紝浠呴噴鏀捐嚜宸辨寔鏈夌殑閿併��
+     */
+    private void unlockAgainTask(Long subId, String token) {
+        if (token == null) {
+            return;
+        }
+        try {
+            String lockKey = AGAIN_LOCK_KEY_PREFIX + subId;
+            Object current = redisCache.getCacheObject(lockKey);
+            if (token.equals(current)) {
+                redisCache.deleteObject(lockKey);
+            }
+        } catch (Exception e) {
+            log.warn("鍐嶆闅忚閲婃斁Redis閿佸け璐�, subId={}", subId, e);
+        }
+    }
+
+    /**
+     * 鎸変换鍔$殑 preachformDesc 鍒濆鍖栧彂閫佹柟寮忔槑缁嗭紙service_subtask_preachform锛夈��
+     * <p>
+     * 杩佺Щ璇存槑锛氬師瀹炵幇鏄� smartor 渚� {@code IPatMedInhospService#addServiceSubtaskPreachform}锛�
+     * 璇诲啓鐨勪袱寮犺〃閮藉睘浜庢湰妯″潡锛屾晠鎸夋ā鍧楄竟鐣屾惉鍒拌繖閲岋紝璇箟淇濇寔涓�鑷达細
+     * 鍏堟寜 subid+preachform 鏌ラ噸锛堝凡瀛樺湪灏变笉鏂板锛夛紝sendstate 榛樿涓� "1"锛堜紶鍙傚彲瑕嗙洊锛夈��
+     *
+     * @param serviceSubtask 宸茶惤搴撶殑瀛愪换鍔�
+     * @param sendstate      鍙戦�佺姸鎬佽鐩栧�硷紝涓虹┖鍒欑敤 "1"
+     * @return 鍥哄畾杩斿洖 0锛堜笌鍘熷疄鐜颁竴鑷达級
+     */
+    private Integer addServiceSubtaskPreachform(ServiceSubtask serviceSubtask, String sendstate) {
+        ServiceTask serviceTask = serviceTaskService.selectServiceTaskByTaskid(serviceSubtask.getTaskid());
+        if (serviceTask == null) {
+            log.error("addServiceSubtaskPreachform taskid={} 鏌ヤ笉鍒颁换鍔★紝璺宠繃preachform瀛愯〃鐢熸垚", serviceSubtask.getTaskid());
+            return 0;
+        }
+        String preachformDesc = serviceTask.getPreachformDesc();
+        if (StringUtils.isEmpty(preachformDesc)) {
+            return 0;
+        }
+        List<ServiceSubtaskPreachform> preachformList;
+        try {
+            preachformList = JSON.parseArray(preachformDesc, ServiceSubtaskPreachform.class);
+        } catch (Exception e) {
+            log.error("preachform杞琇ist<ServiceSubtaskPreachform>鎶ラ敊浜嗭細{}", e.getMessage());
+            return 0;
+        }
+        if (CollectionUtils.isEmpty(preachformList)) {
+            log.error("preachformDesc瑙f瀽鍚庝负绌猴紝璺宠繃preachform瀛愯〃鐢熸垚锛宼askid涓猴細{}锛宲reachformDesc涓猴細{}",
+                    serviceSubtask.getTaskid(), preachformDesc);
+            return 0;
+        }
+        for (ServiceSubtaskPreachform serviceSubtaskPreachform : preachformList) {
+            serviceSubtaskPreachform.setTaskid(serviceSubtask.getTaskid());
+            serviceSubtaskPreachform.setSubid(serviceSubtask.getId());
+            //鍏堟煡璇竴涓嬶紝鏄惁瀛樺湪锛屽鏋滃凡缁忓瓨鍦紝灏变笉鏂板浜�
+            if (CollectionUtils.isNotEmpty(taskPreachformPort.selectServiceSubtaskPreachformList(serviceSubtaskPreachform))) {
+                continue;
+            }
+            serviceSubtaskPreachform.setSendstate(StringUtils.isNotEmpty(sendstate) ? sendstate : "1");
+            serviceSubtaskPreachform.setOrgid(serviceSubtask.getOrgid());
+            taskPreachformPort.insertServiceSubtaskPreachform(serviceSubtaskPreachform);
+        }
+        return 0;
     }
 
     /**
@@ -1059,6 +1572,20 @@
     }
 
     /**
+     * 绉戝缂栫爜鍘婚噸锛屼繚鎸佸師椤哄簭锛岄伩鍏� service_task.deptcode 瓒呴暱鍜� service_taskdept 閲嶅缁戝畾
+     */
+    private String normalizeDeptCodes(String deptCodes) {
+        if (StringUtils.isEmpty(deptCodes)) {
+            return deptCodes;
+        }
+        return Arrays.stream(deptCodes.split(","))
+                .map(String::trim)
+                .filter(code -> StringUtils.isNotEmpty(code))
+                .distinct()
+                .collect(Collectors.joining(","));
+    }
+
+    /**
      * 鍗曚竴浠诲姟
      *
      * @return 缁撴灉
@@ -1070,6 +1597,8 @@
             log.info("浠诲姟鍏ュ弬涓虹┖锛岃妫�鏌ュ叆鍙�");
             throw new BaseException("浠诲姟鍏ュ弬涓虹┖锛岃妫�鏌ュ叆鍙�");
         }
+        // 绉戝缂栫爜鍘婚噸锛岄伩鍏� service_task.deptcode 瓒呴暱鍙� service_taskdept 閲嶅缁戝畾
+        serviceTaskVO.setDeptcode(normalizeDeptCodes(serviceTaskVO.getDeptcode()));
         ServiceTask serviceTask = DtoConversionUtils.sourceToTarget(serviceTaskVO, ServiceTask.class);
         if (CollectionUtils.isEmpty(serviceTaskVO.getPreachformList())) throw new BaseException("浠诲姟鐨勫彂閫佹柟寮忎负绌�");
         //灏唒reachfromList鏀惧埌serviceTask涓殑preachfrom涓�
@@ -1304,7 +1833,12 @@
                     serviceSubtask.setInhospid(patTaskRelevance.getHospid());
                     //鏂板缓瀹屾垚灏辨槸寰呭彂閫佺姸鎬�
                     serviceSubtask.setSendstate(2L);
-                    serviceSubtask.setLeaveldeptcode(StringUtils.isNotEmpty(patTaskRelevance.getDeptcode()) ? patTaskRelevance.getDeptcode() : serviceTaskVO.getDeptcode());
+                    String subtaskDeptCode = patTaskRelevance.getDeptcode();
+                    if (StringUtils.isEmpty(subtaskDeptCode) && StringUtils.isNotEmpty(serviceTaskVO.getDeptcode())) {
+                        // service_subtask.leaveldeptcode 涓哄崟鍊煎瓧娈碉紝涓嶈兘鍐欏叆浠诲姟鐨勫绉戝鎷兼帴涓诧紝鍙栫涓�涓瀹ゅ厹搴�
+                        subtaskDeptCode = serviceTaskVO.getDeptcode().split(",")[0].trim();
+                    }
+                    serviceSubtask.setLeaveldeptcode(subtaskDeptCode);
                     serviceSubtask.setLeaveldeptname(StringUtils.isNotEmpty(patTaskRelevance.getDeptName()) ? patTaskRelevance.getDeptName() : serviceTaskVO.getDeptname());
                     serviceSubtask.setLeavehospitaldistrictcode(patTaskRelevance.getLeavehospitaldistrictcode());
                     serviceSubtask.setLeavehospitaldistrictname(patTaskRelevance.getLeavehospitaldistrictname());
@@ -1377,6 +1911,8 @@
             ServiceTask serviceTask1 = serviceTaskService.selectServiceTaskByTaskid(serviceTask.getTaskid());
             ServiceSubtaskEntity serviceSubtaskVO = new ServiceSubtaskEntity();
             serviceSubtaskVO.setTaskid(serviceTask1.getTaskid());
+            //鍙垹闄ら娆¢殢璁挎湭瀹屾垚鐨�
+            serviceSubtaskVO.setVisitCount(1);
             serviceSubtaskVO.setSendstate(2L);
             List<ServiceSubtask> serviceSubtaskList = serviceSubtaskMapper.selectServiceSubtaskList(serviceSubtaskVO);
             serviceSubtaskVO.setSendstate(1L);
@@ -1442,6 +1978,11 @@
             if (StringUtils.isNotEmpty(serviceTaskVO.getAppltype())) {
                 if (serviceTaskVO.getAppltype().equals("1") || serviceTaskVO.getAppltype().equals("2")) {
                     if (StringUtils.isNotEmpty(serviceTaskVO.getDeptcode())) {
+                        // 淇敼浠诲姟鏃跺厛杞垹闄ゆ棫鐨勭瀹ょ粦瀹氾紝閬垮厤鏃х瀹ゆ畫鐣�
+                        ServiceTaskdept oldDept = new ServiceTaskdept();
+                        oldDept.setTaskId(serviceTask.getTaskid());
+                        oldDept.setDeptType("1");
+                        serviceTaskdeptService.deleteServiceTaskdeptByTaskId(oldDept);
                         String[] deptcodes = serviceTaskVO.getDeptcode().split(",");
                         for (String deptCode : deptcodes) {
                             ServiceTaskdept serviceTaskdept = new ServiceTaskdept();
@@ -2170,6 +2711,8 @@
             serviceSubtaskStatistic.setLeavehospitaldistrictcode(serviceSubtaskList.get(0).getLeavehospitaldistrictcode());
             serviceSubtaskStatistic.setDrcode(serviceSubtaskList.get(0).getDrcode());
             serviceSubtaskStatistic.setDrname(serviceSubtaskList.get(0).getDrname());
+            //闅忚涓績绉戝锛岀敤浜庣粺璁″啀娆¢殢璁挎帹閫佸埌闅忚涓績鐨勪汉鏁�
+            String centerDept = sysConfigService.selectConfigByKey("sys.smartor.center.dept", serviceSubtaskList.get(0).getOrgid());
             //鍑洪櫌浜烘
             serviceSubtaskStatistic.setDischargeCount(serviceSubtaskList.size());
             for (ServiceSubtask serviceSubtask : serviceSubtaskList) {
@@ -2184,9 +2727,31 @@
                 if (serviceSubtask.getSendstate() != null && serviceSubtask.getSendstate() != 4) {
                     serviceSubtaskStatistic.setFollowUpNeeded(serviceSubtaskStatistic.getFollowUpNeeded() + 1L);
                 }
+                //鍙婃椂闅忚浜烘锛氬凡瀹屾垚涓旀湭瓒呮椂
+                if (isTimelyFollowUp(serviceSubtask)) {
+                    serviceSubtaskStatistic.setTimelyFollowUp(serviceSubtaskStatistic.getTimelyFollowUp() + 1L);
+                }
+
+                //棣栨鍑洪櫌浜烘锛歷isitCount = 1锛屼綔涓哄啀娆¢殢璁挎帹閫佺巼鐨勫垎姣�
+                if (serviceSubtask.getVisitCount() != null && serviceSubtask.getVisitCount() == 1) {
+                    serviceSubtaskStatistic.setFirstDischargeCount(serviceSubtaskStatistic.getFirstDischargeCount() + 1L);
+                }
+                //鍐嶆闅忚鎺ㄩ�佷汉鏁帮細棣栦釜鍐嶆闅忚锛坴isitCount = 2锛変笖鎵ц绉戝涓洪殢璁夸腑蹇冪瀹�
+                if (serviceSubtask.getVisitCount() != null && serviceSubtask.getVisitCount() == 2
+                        && StringUtils.isNotEmpty(centerDept)
+                        && StringUtils.equals(StringUtils.trim(centerDept), StringUtils.trim(serviceSubtask.getVisitDeptCode()))) {
+                    serviceSubtaskStatistic.setAgainFollowUpPushCount(serviceSubtaskStatistic.getAgainFollowUpPushCount() + 1L);
+                }
+
+                //浜哄伐宸叉嫧閫氭�婚噺
+                if (ObjectUtils.isNotEmpty(serviceSubtask.getIsManuallyCalled())
+                        && serviceSubtask.getIsManuallyCalled().equals(1L)) {
+                    serviceSubtaskStatistic.setManuallyCalled(serviceSubtaskStatistic.getManuallyCalled() + 1L);
+                }
 
                 //棣栨鍑洪櫌闅忚
                 if (serviceSubtask.getVisitCount() != null && serviceSubtask.getVisitCount() == 1) {
+                    applyFirstFollowUpExtraStatistics(serviceSubtaskStatistic, serviceSubtask);
                     //棣栨搴旈殢璁�
                     if (serviceSubtask.getSendstate() != null && !serviceSubtask.getSendstate().equals(4L)) {
                         serviceSubtaskStatistic.setNeedFollowUp(serviceSubtaskStatistic.getNeedFollowUp() + 1L);
@@ -2308,6 +2873,7 @@
                     }
                 }//浜屾鍑洪櫌闅忚
                 else if (serviceSubtask.getVisitCount() != null && serviceSubtask.getVisitCount() > 1) {
+                    applyAgainFollowUpExtraStatistics(serviceSubtaskStatistic, serviceSubtask);
                     //鍐嶆搴旈殢璁�
                     if (serviceSubtask.getSendstate() != null && serviceSubtask.getSendstate() != 4) {
                         serviceSubtaskStatistic.setNeedFollowUpAgain(serviceSubtaskStatistic.getNeedFollowUpAgain() + 1L);
@@ -2446,6 +3012,80 @@
     }
 
     /**
+     * 鏄惁灞炰簬銆屽強鏃堕殢璁裤�嶏細闅忚宸插畬鎴愪笖瀹屾垚鏃剁姸鎬佷负姝e父瀹屾垚锛堟湭瓒呮椂锛夈��
+     * <p>
+     * finish_state锛�1-姝e父瀹屾垚 2-寤舵湡瀹屾垚 3-瓒呮椂瀹屾垚锛屽彧鏈� 1 璁″叆鍙婃椂闅忚銆�
+     *
+     * @param serviceSubtask 闅忚瀛愪换鍔�
+     * @return true-璁″叆鍙婃椂闅忚浜烘
+     */
+    private boolean isTimelyFollowUp(ServiceSubtask serviceSubtask) {
+        return serviceSubtask.getSendstate() != null && serviceSubtask.getSendstate().equals(6L)
+                && serviceSubtask.getFinishState() != null && serviceSubtask.getFinishState().equals(1L);
+    }
+
+    /**
+     * 甯備竴缁熻鍙e緞锛氶娆″緟闅忚銆佸凡鍙戦�佸強浜哄伐鐢佃瘽瓒呮椂銆�
+     */
+    private void applyFirstFollowUpExtraStatistics(ServiceSubtaskStatistic statistic, ServiceSubtask serviceSubtask) {
+        if (serviceSubtask.getVisitCount() == null || serviceSubtask.getVisitCount() != 1 || serviceSubtask.getSendstate() == null) {
+            return;
+        }
+        Long sendstate = serviceSubtask.getSendstate();
+        if (sendstate.equals(3L)) {
+            statistic.setSent(statistic.getSent() + 1L);
+        }
+        if (sendstate.equals(7L) && isManuallyNotCalled(serviceSubtask)) {
+            statistic.setManualCallTimeout(statistic.getManualCallTimeout() + 1L);
+        }
+        if (sendstate.equals(1L) || sendstate.equals(2L) || sendstate.equals(3L) || sendstate.equals(7L)) {
+            statistic.setPendingFollowUpAll(statistic.getPendingFollowUpAll() + 1L);
+        }
+        if (sendstate.equals(1L) || sendstate.equals(2L)) {
+            statistic.setPendingFollowUpWaitSend(statistic.getPendingFollowUpWaitSend() + 1L);
+        }
+        if (sendstate.equals(3L) || sendstate.equals(7L)) {
+            statistic.setPendingFollowUpSent(statistic.getPendingFollowUpSent() + 1L);
+        }
+    }
+
+    /**
+     * 鍐嶆闅忚闄勫姞缁熻锛氬緟闅忚銆佸凡鍙戦�併�佷汉宸ョ數璇濊秴鏃跺強鍥為櫌澶嶈瘖澶嶆煡锛坰uggest=6锛夈��
+     */
+    private void applyAgainFollowUpExtraStatistics(ServiceSubtaskStatistic statistic, ServiceSubtask serviceSubtask) {
+        if (serviceSubtask.getVisitCount() == null || serviceSubtask.getVisitCount() <= 1) {
+            return;
+        }
+        if (serviceSubtask.getSendstate() == null) {
+            return;
+        }
+        Long sendstate = serviceSubtask.getSendstate();
+        // 鍥為櫌澶嶈瘖澶嶆煡锛氬啀娆¢殢璁夸腑鎸夊鐞嗘剰瑙� suggest=6 缁熻
+        if (StringUtils.equals("6", StringUtils.trim(serviceSubtask.getSuggest()))) {
+            statistic.setSuggest6CountAgain(statistic.getSuggest6CountAgain() + 1L);
+        }
+        if (sendstate.equals(3L)) {
+            statistic.setSentAgain(statistic.getSentAgain() + 1L);
+        }
+        if (sendstate.equals(7L) && isManuallyNotCalled(serviceSubtask)) {
+            statistic.setManualCallTimeoutAgain(statistic.getManualCallTimeoutAgain() + 1L);
+        }
+        if (sendstate.equals(1L) || sendstate.equals(2L) || sendstate.equals(3L) || sendstate.equals(7L)) {
+            statistic.setPendingFollowUpAllAgain(statistic.getPendingFollowUpAllAgain() + 1L);
+        }
+        if (sendstate.equals(1L) || sendstate.equals(2L)) {
+            statistic.setPendingFollowUpWaitSendAgain(statistic.getPendingFollowUpWaitSendAgain() + 1L);
+        }
+        if (sendstate.equals(3L) || sendstate.equals(7L)) {
+            statistic.setPendingFollowUpSentAgain(statistic.getPendingFollowUpSentAgain() + 1L);
+        }
+    }
+
+    private boolean isManuallyNotCalled(ServiceSubtask serviceSubtask) {
+        return serviceSubtask.getIsManuallyCalled() != null && serviceSubtask.getIsManuallyCalled().equals(0L);
+    }
+
+    /**
      * 棣栨闅忚缁熻
      *
      * @param serviceSubtaskList
@@ -2466,6 +3106,7 @@
         serviceSubtaskStatistic.setDischargeCount(serviceSubtaskList.size());
 
         for (ServiceSubtask serviceSubtask : serviceSubtaskList) {
+            applyFirstFollowUpExtraStatistics(serviceSubtaskStatistic, serviceSubtask);
             //鏃犻渶闅忚浜烘
             if (serviceSubtask.getSendstate() != null && serviceSubtask.getSendstate() == 4) {
                 serviceSubtaskStatistic.setNonFollowUp(serviceSubtaskStatistic.getNonFollowUp() + 1L);
@@ -2476,6 +3117,10 @@
             //搴旈殢璁夸汉娆�
             if (serviceSubtask.getSendstate() != null && serviceSubtask.getSendstate() != 4) {
                 serviceSubtaskStatistic.setFollowUpNeeded(serviceSubtaskStatistic.getFollowUpNeeded() + 1L);
+            }
+            //鍙婃椂闅忚浜烘锛氬凡瀹屾垚涓旀湭瓒呮椂
+            if (isTimelyFollowUp(serviceSubtask)) {
+                serviceSubtaskStatistic.setTimelyFollowUp(serviceSubtaskStatistic.getTimelyFollowUp() + 1L);
             }
 
             //棣栨鍑洪櫌闅忚
@@ -2635,6 +3280,7 @@
         //鍑洪櫌浜烘
         serviceSubtaskStatistic.setDischargeCount(serviceSubtaskList.size());
         for (ServiceSubtask serviceSubtask : serviceSubtaskList) {
+            applyAgainFollowUpExtraStatistics(serviceSubtaskStatistic, serviceSubtask);
             //鏃犻渶闅忚浜烘
             if (serviceSubtask.getSendstate() != null && serviceSubtask.getSendstate() == 4) {
                 serviceSubtaskStatistic.setNonFollowUp(serviceSubtaskStatistic.getNonFollowUp() + 1L);
@@ -2645,6 +3291,10 @@
             //搴旈殢璁夸汉娆�
             if (serviceSubtask.getSendstate() != null && serviceSubtask.getSendstate() != 4) {
                 serviceSubtaskStatistic.setFollowUpNeeded(serviceSubtaskStatistic.getFollowUpNeeded() + 1L);
+            }
+            //鍙婃椂闅忚浜烘锛氬凡瀹屾垚涓旀湭瓒呮椂
+            if (isTimelyFollowUp(serviceSubtask)) {
+                serviceSubtaskStatistic.setTimelyFollowUp(serviceSubtaskStatistic.getTimelyFollowUp() + 1L);
             }
 
             //鍐嶆鍑洪櫌闅忚
@@ -2901,7 +3551,8 @@
         serviceSubtaskStatistic.setDrname(serviceSubtaskList.get(0).getDrname());
         serviceSubtaskStatistic.setJoyCount(0);
         serviceSubtaskStatistic.setJoyAllCount(0);
-
+        //鍑洪櫌浜烘
+        serviceSubtaskStatistic.setDischargeCount(serviceSubtaskList.size());
         //鑾峰彇闅忚缁熻
         if (subCounts == null) {
             subCounts = new ArrayList<>();
@@ -3010,11 +3661,14 @@
      * 鑾峰彇闅忚缁熻瓒呴摼鎺�
      * dischargeCountInfo
      * followUpNeededAllInfo - sendstate != 4 (1,2,3,5,6,7)
+     * firstDischargeCountInfo - visitCount = 1
+     * againFollowUpPushCountInfo - visitCount = 2 && visitDeptCode = sys.smartor.center.dept锛堥殢璁夸腑蹇冪瀹わ級
      * nonFollowUpInfo - sendstate = 4
      * needFollowUpInfo - visitCount = 1 && sendstate != 4
      * pendingFollowUpInfo - visitCount = 1 && sendstate = 2
      * followUpSuccessInfo - visitCount = 1 && sendstate = 6
      * followUpFailInfo - visitCount = 1 && (sendstate = 5 || sendstate = 7)
+     * timelyFollowUpInfo - sendstate = 6 && finish_state = 1锛堟部鐢ㄨ姹傜殑 visitCount 鍙e緞锛�
      * manualInfo - visitCount = 1 && currentPreachForm = "1"
      * voiceInfo - visitCount = 1 && currentPreachForm = "3"
      * smsInfo - visitCount = 1 && currentPreachForm = "4"
@@ -3043,6 +3697,18 @@
      * taskSituation4Info - taskSituation = 4
      * taskSituation5Info - taskSituation = 5
      * taskSituation6Info - taskSituation = 7
+     * manuallyCalledInfo - isManuallyCalled = 1L
+     * manualCallTimeoutInfo - visitCount = 1 && sendstate = 7 && isManuallyCalled = 0
+     * manualCallTimeoutAgainInfo - visitCount > 1 && sendstate = 7 && isManuallyCalled = 0
+     * pendingFollowUpAllInfo - visitCount = 1 && sendstate in (1,2,3,7)
+     * pendingFollowUpAllAgainInfo - visitCount > 1 && sendstate in (1,2,3,7)
+     * pendingFollowUpWaitSendInfo - visitCount = 1 && sendstate in (1,2)
+     * pendingFollowUpWaitSendAgainInfo - visitCount > 1 && sendstate in (1,2)
+     * pendingFollowUpSentInfo - visitCount = 1 && sendstate in (3,7)
+     * pendingFollowUpSentAgainInfo - visitCount > 1 && sendstate in (3,7)
+     * sentInfo - visitCount = 1 && sendstate = 3
+     * sentAgainInfo - visitCount > 1 && sendstate = 3
+     * suggest6CountAgainInfo - visitCount > 1 && suggest = 6
      * filterCountList - taskSituation = 6 && sendstate = 4
      *
      */
@@ -3050,7 +3716,10 @@
     public List<ServiceSubtask> getSfStatisticsHyperlink(ServiceSubtaskCountReq serviceSubtaskCountReq) {
         String hyperLinkInfoType = serviceSubtaskCountReq.getHyperLinkInfoType();
         if (StringUtils.isNotEmpty(hyperLinkInfoType)) {
-            applyHyperLinkInfoType(serviceSubtaskCountReq, hyperLinkInfoType);
+            if (!applyHyperLinkInfoType(serviceSubtaskCountReq, hyperLinkInfoType)) {
+                // 涓嬮捇渚濊禆鐨勯厤缃己澶辨椂杩斿洖绌烘槑缁嗭紝閬垮厤閫�鍖栨垚鏃犺繃婊ゆ潯浠剁殑鏌ヨ
+                return new ArrayList<>();
+            }
         }
 
         String followUpCountStyle = serviceSubtaskCountReq.getFollowUpCountStyle();
@@ -3071,15 +3740,40 @@
         return serviceSubtasks;
     }
 
-    private void applyHyperLinkInfoType(ServiceSubtaskCountReq serviceSubtaskCountReq, String hyperLinkInfoType) {
+    private boolean applyHyperLinkInfoType(ServiceSubtaskCountReq serviceSubtaskCountReq, String hyperLinkInfoType) {
+        //鍙婃椂闅忚鏄庣粏闇�瑕佹部鐢ㄨ姹傜殑棣栨/鍐嶆鍙e緞
+        Integer originVisitCount = serviceSubtaskCountReq.getVisitCount();
         serviceSubtaskCountReq.setVisitCount(null);
         serviceSubtaskCountReq.setSendstates(null);
         serviceSubtaskCountReq.setCurrentPreachform(null);
         serviceSubtaskCountReq.setExcep(null);
         serviceSubtaskCountReq.setTaskSituation(null);
+        serviceSubtaskCountReq.setFinishState(null);
+        serviceSubtaskCountReq.setSuggest(null);
 
         switch (hyperLinkInfoType) {
             case "dischargeCountInfo":
+                break;
+            case "firstDischargeCountInfo":
+                //棣栨鍑洪櫌浜烘锛歷isitCount = 1
+                serviceSubtaskCountReq.setVisitCount(1);
+                break;
+            case "againFollowUpPushCountInfo": {
+                //鍐嶆闅忚鎺ㄩ�佷汉鏁帮細visitCount = 2 涓旀墽琛岀瀹や负闅忚涓績绉戝
+                String centerDept = sysConfigService.selectConfigByKey("sys.smartor.center.dept", serviceSubtaskCountReq.getOrgid());
+                if (StringUtils.isBlank(centerDept)) {
+                    log.warn("鑾峰彇鍐嶆闅忚鎺ㄩ�佹槑缁嗗け璐ワ紝鏈厤缃殢璁夸腑蹇冪瀹ゅ弬鏁� sys.smartor.center.dept, orgid={}", serviceSubtaskCountReq.getOrgid());
+                    return false;
+                }
+                serviceSubtaskCountReq.setVisitCount(2);
+                serviceSubtaskCountReq.setVisitDeptCode(centerDept.trim());
+                serviceSubtaskCountReq.setVisitDeptName(null);
+                break;
+            }
+            case "timelyFollowUpInfo":
+                serviceSubtaskCountReq.setVisitCount(originVisitCount);
+                serviceSubtaskCountReq.setSendstates(new ArrayList<>(Collections.singletonList(6L)));
+                serviceSubtaskCountReq.setFinishState(1L);
                 break;
             case "followUpNeededAllInfo":
                 serviceSubtaskCountReq.setSendstates(new ArrayList<>(Arrays.asList(1L, 2L, 3L, 5L, 6L, 7L)));
@@ -3115,7 +3809,7 @@
                 serviceSubtaskCountReq.setVisitCount(1);
                 serviceSubtaskCountReq.setCurrentPreachform("4");
                 break;
-            case "wechatInfo":
+            case "weChatInfo":
                 serviceSubtaskCountReq.setVisitCount(1);
                 serviceSubtaskCountReq.setCurrentPreachform("5");
                 break;
@@ -3134,7 +3828,7 @@
                 serviceSubtaskCountReq.setSendstates(new ArrayList<>(Collections.singletonList(6L)));
                 serviceSubtaskCountReq.setCurrentPreachform("4");
                 break;
-            case "wechatSuccessInfo":
+            case "weChatSuccessInfo":
                 serviceSubtaskCountReq.setVisitCount(1);
                 serviceSubtaskCountReq.setSendstates(new ArrayList<>(Collections.singletonList(6L)));
                 serviceSubtaskCountReq.setCurrentPreachform("5");
@@ -3171,7 +3865,7 @@
                 serviceSubtaskCountReq.setVisitCount(2);
                 serviceSubtaskCountReq.setCurrentPreachform("4");
                 break;
-            case "wechatAgainInfo":
+            case "weChatAgainInfo":
                 serviceSubtaskCountReq.setVisitCount(2);
                 serviceSubtaskCountReq.setCurrentPreachform("5");
                 break;
@@ -3190,7 +3884,7 @@
                 serviceSubtaskCountReq.setSendstates(new ArrayList<>(Collections.singletonList(6L)));
                 serviceSubtaskCountReq.setCurrentPreachform("4");
                 break;
-            case "wechatAgainSuccessInfo":
+            case "weChatAgainSuccessInfo":
                 serviceSubtaskCountReq.setVisitCount(2);
                 serviceSubtaskCountReq.setSendstates(new ArrayList<>(Collections.singletonList(6L)));
                 serviceSubtaskCountReq.setCurrentPreachform("5");
@@ -3198,6 +3892,11 @@
             case "abnormalAgainInfo":
                 serviceSubtaskCountReq.setVisitCount(2);
                 serviceSubtaskCountReq.setExcep("1");
+                break;
+            case "suggest6CountAgainInfo":
+                // 鍐嶆闅忚鍥為櫌澶嶈瘖澶嶆煡锛歷isitCount > 1 涓� suggest = 6
+                serviceSubtaskCountReq.setVisitCount(2);
+                serviceSubtaskCountReq.setSuggest("6");
                 break;
             case "taskSituation1Info":
                 serviceSubtaskCountReq.setTaskSituation(1);
@@ -3221,9 +3920,55 @@
                 serviceSubtaskCountReq.setTaskSituation(6);
                 serviceSubtaskCountReq.setSendstates(new ArrayList<>(Collections.singletonList(4L)));
                 break;
+            case "manuallyCalledInfo":
+                serviceSubtaskCountReq.setIsManuallyCalled(1L);
+                break;
+            case "manualCallTimeoutInfo":
+                serviceSubtaskCountReq.setVisitCount(1);
+                serviceSubtaskCountReq.setSendstates(new ArrayList<>(Collections.singletonList(7L)));
+                serviceSubtaskCountReq.setIsManuallyCalled(0L);
+                break;
+            case "manualCallTimeoutAgainInfo":
+                serviceSubtaskCountReq.setVisitCount(2);
+                serviceSubtaskCountReq.setSendstates(new ArrayList<>(Collections.singletonList(7L)));
+                serviceSubtaskCountReq.setIsManuallyCalled(0L);
+                break;
+            case "pendingFollowUpAllInfo":
+                serviceSubtaskCountReq.setVisitCount(1);
+                serviceSubtaskCountReq.setSendstates(new ArrayList<>(Arrays.asList(1L, 2L, 3L, 7L)));
+                break;
+            case "pendingFollowUpAllAgainInfo":
+                serviceSubtaskCountReq.setVisitCount(2);
+                serviceSubtaskCountReq.setSendstates(new ArrayList<>(Arrays.asList(1L, 2L, 3L, 7L)));
+                break;
+            case "pendingFollowUpWaitSendInfo":
+                serviceSubtaskCountReq.setVisitCount(1);
+                serviceSubtaskCountReq.setSendstates(new ArrayList<>(Arrays.asList(1L, 2L)));
+                break;
+            case "pendingFollowUpWaitSendAgainInfo":
+                serviceSubtaskCountReq.setVisitCount(2);
+                serviceSubtaskCountReq.setSendstates(new ArrayList<>(Arrays.asList(1L, 2L)));
+                break;
+            case "pendingFollowUpSentInfo":
+                serviceSubtaskCountReq.setVisitCount(1);
+                serviceSubtaskCountReq.setSendstates(new ArrayList<>(Arrays.asList(3L, 7L)));
+                break;
+            case "pendingFollowUpSentAgainInfo":
+                serviceSubtaskCountReq.setVisitCount(2);
+                serviceSubtaskCountReq.setSendstates(new ArrayList<>(Arrays.asList(3L, 7L)));
+                break;
+            case "sentInfo":
+                serviceSubtaskCountReq.setVisitCount(1);
+                serviceSubtaskCountReq.setSendstates(new ArrayList<>(Collections.singletonList(3L)));
+                break;
+            case "sentAgainInfo":
+                serviceSubtaskCountReq.setVisitCount(2);
+                serviceSubtaskCountReq.setSendstates(new ArrayList<>(Collections.singletonList(3L)));
+                break;
             default:
                 break;
         }
+        return true;
     }
 
     /**
@@ -3233,7 +3978,7 @@
      * @return
      */
     @Override
-    @Cacheable(value = "getSfStatisticsScript", key = "T(org.springframework.util.DigestUtils).md5DigestAsHex(#serviceSubtaskCountReq.toString().getBytes())", unless = "#result == null or #result.isEmpty()")
+//    @Cacheable(value = "getSfStatisticsScript", key = "T(org.springframework.util.DigestUtils).md5DigestAsHex(#serviceSubtaskCountReq.toString().getBytes())", unless = "#result == null or #result.isEmpty()")
     public List<ServiceSubtaskStatistic> getSfStatisticsScript(ServiceSubtaskCountReq serviceSubtaskCountReq) {
         log.info("getSfStatisticsScript鐨勫叆鍙備负锛歿}", serviceSubtaskCountReq);
         List<ServiceSubtaskStatistic> serviceSubtaskStatistics = new ArrayList<>();
@@ -3254,6 +3999,12 @@
             groupKey = hasDrcode ? "drcode" : "deptcode";
         } else if (serviceSubtaskCountReq.getDeptcodes() != null && !serviceSubtaskCountReq.getDeptcodes().isEmpty()) {
             // 鍘嗗彶鍏ュ弬 deptcodes 琛ㄧず绂婚櫌绉戝
+            groupKey = hasDrcode ? "drcode" : "leaveldeptcode";
+        } else if (serviceSubtaskCountReq.getStatisticaltype() != null && serviceSubtaskCountReq.getStatisticaltype() == 1) {
+            // 鍘嗗彶鍏ュ弬锛堝競涓�绛夎�佸墠绔彧浼� statisticaltype锛夛細1 = 鐥呭尯缁熻
+            groupKey = hasDrcode ? "drcode" : "leavehospitaldistrictcode";
+        } else if (serviceSubtaskCountReq.getStatisticaltype() != null && serviceSubtaskCountReq.getStatisticaltype() == 2) {
+            // 鍘嗗彶鍏ュ弬锛�2 = 绉戝缁熻锛堝師 deptcode 鍗崇幇鍦ㄧ殑绂婚櫌绉戝 leaveldeptcode锛�
             groupKey = hasDrcode ? "drcode" : "leaveldeptcode";
         }
         serviceSubtaskCountReq.setGroupKey(groupKey);
@@ -3441,8 +4192,7 @@
     }
 
     @Override
-    public Map<String, ServiceSubtaskScriptCount> getSfStatisticsScriptDetails(ServiceSubtaskCountReq serviceSubtaskCountReq) {
-        Map<String, ServiceSubtaskScriptCount> result = new HashMap<>();
+    public Map<String, Object> getSfStatisticsScriptDetails(ServiceSubtaskCountReq serviceSubtaskCountReq) {
         List<ServiceSubtaskScriptCount> allCounts = new ArrayList<>();
         List<ServiceSubtaskScriptCount> allItemCounts = new ArrayList<>();
         String type = serviceSubtaskCountReq.getType();
@@ -3466,7 +4216,23 @@
             allItemCounts = serviceSubtaskMapper.getSfStatisticsScriptItemDetails(serviceSubtaskCountReq);
         }
 
-        for (ServiceSubtaskScriptCount count : allCounts) {
+        //杩斿洖缁撴瀯鏄� Map锛屼笖涓嶄紶 type 鏃朵細鎵ц涓ゆ潯 SQL锛孭ageHelper 娌℃硶瀵规暣浣撳垎椤碉紝
+        //鎵�浠ュ湪棰樼洰鍚堝苟瀹屾垚涔嬪悗鎸夐鐩紙scriptid锛夊湪杩欓噷鍒嗛〉锛屼繚璇� total 鍜屾瘡椤垫暟鎹兘鏄鐩淮搴︾殑銆�
+        allCounts.sort(Comparator
+                .comparing(ServiceSubtaskScriptCount::getSubtaskType, Comparator.nullsLast(String::compareTo))
+                .thenComparing(ServiceSubtaskScriptCount::getScriptid, Comparator.nullsLast(Long::compareTo)));
+
+        int pageNum = ObjectUtils.isNotEmpty(serviceSubtaskCountReq.getPageNum()) && serviceSubtaskCountReq.getPageNum() > 0
+                ? serviceSubtaskCountReq.getPageNum() : 1;
+        int pageSize = ObjectUtils.isNotEmpty(serviceSubtaskCountReq.getPageSize()) && serviceSubtaskCountReq.getPageSize() > 0
+                ? serviceSubtaskCountReq.getPageSize() : 10;
+        int total = allCounts.size();
+        int fromIndex = (int) Math.min((long) (pageNum - 1) * pageSize, total);
+        int toIndex = Math.min(fromIndex + pageSize, total);
+        List<ServiceSubtaskScriptCount> pageCounts = allCounts.subList(fromIndex, toIndex);
+
+        Map<String, ServiceSubtaskScriptCount> pageData = new LinkedHashMap<>();
+        for (ServiceSubtaskScriptCount count : pageCounts) {
             Map<String, ServiceSubtaskScriptCount> itemCountMap = new HashMap<>();
             //闂嵎鍔犱笂绌虹櫧閫夐」
             if (ObjectUtils.isNotEmpty(count.getTemplateid()) && ObjectUtils.isNotEmpty(count.getSubtaskType()) && count.getSubtaskType().equals("2")) {
@@ -3497,31 +4263,42 @@
                 }
             }
             allItemCounts.forEach(itemCount -> {
-                if (ObjectUtils.isNotEmpty(count.getScriptid()) && ObjectUtils.isNotEmpty(itemCount.getScriptid())) {
-                    if (count.getScriptid().equals(itemCount.getScriptid())) {
-                        //鏀惧叆缁熻淇℃伅
-                        itemCount.setScriptContent(count.getScriptContent());
-                        itemCount.setScriptType(count.getScriptType());
-                        itemCount.setSubtaskType(count.getSubtaskType());
-                        itemCount.setCompletedQuantity(count.getCompletedQuantity());
-                        itemCount.setAllQuantity(count.getAllQuantity());
-                        itemCount.setCompletedPercentage(count.getCompletedPercentage());
-                        itemCount.setTemplateid(count.getTemplateid());
-                        //璁$畻閫夋嫨姣斾緥
-                        int chosenQuantity = ObjectUtils.isNotEmpty(itemCount.getChosenQuantity()) ? itemCount.getChosenQuantity() : 0;
-                        int completedQuantity = itemCount.getCompletedQuantity();
-                        if (ObjectUtils.isNotEmpty(completedQuantity) && completedQuantity != 0) {
-                            BigDecimal chosenPercentage = (new BigDecimal(chosenQuantity)).divide(new BigDecimal(completedQuantity), 2, RoundingMode.HALF_UP);
-                            itemCount.setChosenPercentage(chosenPercentage.toString());
-                        }
-                        itemCountMap.put(itemCount.getOptionText(), itemCount);
+                //鏄庣粏琛屽凡鎸夆�滈鐩唴瀹�+浠诲姟绫诲瀷鈥濊仛鍚堝苟甯﹀嚭 script_content锛屼笉鑳界敤 scriptid 鍖归厤锛�
+                //鍚屼竴閬撻浼氳澶嶅埗鍒板涓换鍔℃ā鏉匡紝姣忎釜妯℃澘鐨� scriptid 閮戒笉鍚�
+                boolean sameQuestion = ObjectUtils.isNotEmpty(count.getScriptContent())
+                        && ObjectUtils.isNotEmpty(itemCount.getScriptContent())
+                        && count.getScriptContent().equals(itemCount.getScriptContent())
+                        && Objects.equals(count.getSubtaskType(), itemCount.getSubtaskType());
+                if (sameQuestion) {
+                    //鏀惧叆缁熻淇℃伅
+                    itemCount.setScriptContent(count.getScriptContent());
+                    itemCount.setScriptType(count.getScriptType());
+                    itemCount.setSubtaskType(count.getSubtaskType());
+                    itemCount.setCompletedQuantity(count.getCompletedQuantity());
+                    itemCount.setAllQuantity(count.getAllQuantity());
+                    itemCount.setCompletedPercentage(count.getCompletedPercentage());
+                    itemCount.setTemplateid(count.getTemplateid());
+                    //璁$畻閫夋嫨姣斾緥
+                    int chosenQuantity = ObjectUtils.isNotEmpty(itemCount.getChosenQuantity()) ? itemCount.getChosenQuantity() : 0;
+                    int completedQuantity = itemCount.getCompletedQuantity();
+                    if (ObjectUtils.isNotEmpty(completedQuantity) && completedQuantity != 0) {
+                        BigDecimal chosenPercentage = (new BigDecimal(chosenQuantity)).divide(new BigDecimal(completedQuantity), 2, RoundingMode.HALF_UP);
+                        itemCount.setChosenPercentage(chosenPercentage.toString());
                     }
+                    itemCountMap.put(itemCount.getOptionText(), itemCount);
                 }
             });
             List<ServiceSubtaskScriptCount> itemCounts = new ArrayList<>(itemCountMap.values());
             count.setDetails(itemCounts);
-            result.put(count.getScriptid().toString(), count);
+            pageData.put(count.getScriptid().toString(), count);
         }
+        Map<String, Object> result = new HashMap<>();
+        // total 鏄鐩�绘暟锛堝凡鎸夐鐩悎骞讹級锛宺ows/data 鏄綋鍓嶉〉鏁版嵁
+        result.put("total", total);
+        result.put("pageNum", pageNum);
+        result.put("pageSize", pageSize);
+        result.put("rows", new ArrayList<>(pageCounts));
+        result.put("data", pageData);
         return result;
     }
 
@@ -3536,6 +4313,8 @@
 
         // 鍒濆鍖栨暟鍊煎瓧娈�
         total.setDischargeCount(0);
+        total.setFirstDischargeCount(0L);
+        total.setAgainFollowUpPushCount(0L);
         total.setNonFollowUp(0L);
         total.setFilterCount(0L);
         total.setFollowUpNeeded(0L);
@@ -3543,14 +4322,26 @@
         total.setPendingFollowUp(0L);
         total.setFollowUpSuccess(0L);
         total.setFollowUpFail(0L);
+        total.setTimelyFollowUp(0L);
         total.setManual(0L);
         total.setSms(0L);
         total.setWeChat(0L);
         total.setAbnormal(0L);
         total.setNeedFollowUpAgain(0L);
         total.setPendingFollowUpAgain(0L);
+        total.setManualCallTimeout(0L);
+        total.setManualCallTimeoutAgain(0L);
+        total.setPendingFollowUpAll(0L);
+        total.setPendingFollowUpAllAgain(0L);
+        total.setPendingFollowUpWaitSend(0L);
+        total.setPendingFollowUpWaitSendAgain(0L);
+        total.setPendingFollowUpSent(0L);
+        total.setPendingFollowUpSentAgain(0L);
+        total.setSent(0L);
+        total.setSentAgain(0L);
         total.setFollowUpSuccessAgain(0L);
         total.setFollowUpFailAgain(0L);
+        total.setSuggest6CountAgain(0L);
         total.setManualAgain(0L);
         total.setSmsAgain(0L);
         total.setWeChatAgain(0L);
@@ -3567,6 +4358,8 @@
             if (stat.getDischargeCount() != null) {
                 total.setDischargeCount(total.getDischargeCount() + stat.getDischargeCount());
             }
+            total.setFirstDischargeCount(total.getFirstDischargeCount() + (stat.getFirstDischargeCount() != null ? stat.getFirstDischargeCount() : 0L));
+            total.setAgainFollowUpPushCount(total.getAgainFollowUpPushCount() + (stat.getAgainFollowUpPushCount() != null ? stat.getAgainFollowUpPushCount() : 0L));
             total.setNonFollowUp(total.getNonFollowUp() + (stat.getNonFollowUp() != null ? stat.getNonFollowUp() : 0L));
             total.setFilterCount(total.getFilterCount() + (stat.getFilterCount() != null ? stat.getFilterCount() : 0L));
             total.setFollowUpNeeded(total.getFollowUpNeeded() + (stat.getFollowUpNeeded() != null ? stat.getFollowUpNeeded() : 0L));
@@ -3574,14 +4367,26 @@
             total.setPendingFollowUp(total.getPendingFollowUp() + (stat.getPendingFollowUp() != null ? stat.getPendingFollowUp() : 0L));
             total.setFollowUpSuccess(total.getFollowUpSuccess() + (stat.getFollowUpSuccess() != null ? stat.getFollowUpSuccess() : 0L));
             total.setFollowUpFail(total.getFollowUpFail() + (stat.getFollowUpFail() != null ? stat.getFollowUpFail() : 0L));
+            total.setTimelyFollowUp(total.getTimelyFollowUp() + (stat.getTimelyFollowUp() != null ? stat.getTimelyFollowUp() : 0L));
             total.setManual(total.getManual() + (stat.getManual() != null ? stat.getManual() : 0L));
             total.setSms(total.getSms() + (stat.getSms() != null ? stat.getSms() : 0L));
             total.setWeChat(total.getWeChat() + (stat.getWeChat() != null ? stat.getWeChat() : 0L));
             total.setAbnormal(total.getAbnormal() + (stat.getAbnormal() != null ? stat.getAbnormal() : 0L));
             total.setNeedFollowUpAgain(total.getNeedFollowUpAgain() + (stat.getNeedFollowUpAgain() != null ? stat.getNeedFollowUpAgain() : 0L));
             total.setPendingFollowUpAgain(total.getPendingFollowUpAgain() + (stat.getPendingFollowUpAgain() != null ? stat.getPendingFollowUpAgain() : 0L));
+            total.setManualCallTimeout(total.getManualCallTimeout() + (stat.getManualCallTimeout() != null ? stat.getManualCallTimeout() : 0L));
+            total.setManualCallTimeoutAgain(total.getManualCallTimeoutAgain() + (stat.getManualCallTimeoutAgain() != null ? stat.getManualCallTimeoutAgain() : 0L));
+            total.setPendingFollowUpAll(total.getPendingFollowUpAll() + (stat.getPendingFollowUpAll() != null ? stat.getPendingFollowUpAll() : 0L));
+            total.setPendingFollowUpAllAgain(total.getPendingFollowUpAllAgain() + (stat.getPendingFollowUpAllAgain() != null ? stat.getPendingFollowUpAllAgain() : 0L));
+            total.setPendingFollowUpWaitSend(total.getPendingFollowUpWaitSend() + (stat.getPendingFollowUpWaitSend() != null ? stat.getPendingFollowUpWaitSend() : 0L));
+            total.setPendingFollowUpWaitSendAgain(total.getPendingFollowUpWaitSendAgain() + (stat.getPendingFollowUpWaitSendAgain() != null ? stat.getPendingFollowUpWaitSendAgain() : 0L));
+            total.setPendingFollowUpSent(total.getPendingFollowUpSent() + (stat.getPendingFollowUpSent() != null ? stat.getPendingFollowUpSent() : 0L));
+            total.setPendingFollowUpSentAgain(total.getPendingFollowUpSentAgain() + (stat.getPendingFollowUpSentAgain() != null ? stat.getPendingFollowUpSentAgain() : 0L));
+            total.setSent(total.getSent() + (stat.getSent() != null ? stat.getSent() : 0L));
+            total.setSentAgain(total.getSentAgain() + (stat.getSentAgain() != null ? stat.getSentAgain() : 0L));
             total.setFollowUpSuccessAgain(total.getFollowUpSuccessAgain() + (stat.getFollowUpSuccessAgain() != null ? stat.getFollowUpSuccessAgain() : 0L));
             total.setFollowUpFailAgain(total.getFollowUpFailAgain() + (stat.getFollowUpFailAgain() != null ? stat.getFollowUpFailAgain() : 0L));
+            total.setSuggest6CountAgain(total.getSuggest6CountAgain() + (stat.getSuggest6CountAgain() != null ? stat.getSuggest6CountAgain() : 0L));
             total.setManualAgain(total.getManualAgain() + (stat.getManualAgain() != null ? stat.getManualAgain() : 0L));
             total.setSmsAgain(total.getSmsAgain() + (stat.getSmsAgain() != null ? stat.getSmsAgain() : 0L));
             total.setWeChatAgain(total.getWeChatAgain() + (stat.getWeChatAgain() != null ? stat.getWeChatAgain() : 0L));
@@ -3972,12 +4777,6 @@
         String subId = rsaPublicKeyExample.encryptedData(serviceSubtask.getId().toString(), pub_key);
         String taskId = rsaPublicKeyExample.encryptedData(serviceSubtask.getTaskid().toString(), pub_key);
 
-        //涓芥按涓尰闄㈠彂閫佺殑鏄井淇″叕浼楀彿
-        if (StringUtils.isNotEmpty(serviceSubtask.getOrgid()) && serviceSubtask.getOrgid().equals("47231022633110211A2101")) {
-            resultMap = wechatSubTask(subid);
-            return resultMap;
-        }
-
         //鍙戦�佹暟鎹殑灏佽
         ServiceOutPath serviceOutPath = new ServiceOutPath();
         serviceOutPath.setParam1(taskId);
@@ -4025,6 +4824,25 @@
         return resultMap;
     }
 
+    @Override
+    public Map<String, Object> weChatSubTask(Long subid) {
+        log.info("smsSubTask鐨勫叆鍙備负锛歿}", subid);
+        Map<String, Object> resultMap = new HashMap<>();
+        resultMap.put("code", "500");
+        resultMap.put("msg", "鍙戦�佸け璐�");
+
+        //鑾峰彇鎮h�呯殑鍩烘湰淇℃伅
+        ServiceSubtask serviceSubtask = selectServiceSubtaskById(subid);
+        RSAPublicKeyExample rsaPublicKeyExample = new RSAPublicKeyExample();
+        String patid = rsaPublicKeyExample.encryptedData(serviceSubtask.getPatid().toString(), pub_key);
+        String subId = rsaPublicKeyExample.encryptedData(serviceSubtask.getId().toString(), pub_key);
+        String taskId = rsaPublicKeyExample.encryptedData(serviceSubtask.getTaskid().toString(), pub_key);
+
+        //涓芥按涓尰闄㈠彂閫佺殑鏄井淇″叕浼楀彿
+        resultMap = wechatSubTask(subid);
+        return resultMap;
+    }
+
     public Map<String,Object> wechatSubTask(Long subid){
         log.info("wechatSubTask鐨勫叆鍙備负锛歿}", subid);
         Map<String, Object> resultMap = new HashMap<>();
@@ -4058,8 +4876,16 @@
             serviceOutPath.setUrl(url);
             iServiceOutPathService.updateServiceOutPath(serviceOutPath);
 
-            //his鎮h�卛d鏍¢獙锛堜笌鍘熷疄鐜颁竴鑷达細鍏堟牎楠岃繖涓�椤癸級
-            if (StringUtils.isEmpty(serviceSubtask.getPatidHis())) {
+
+            if (patArchive == null) {
+                resultMap.put("code", -1);
+                resultMap.put("message", "鍙戦�佸紓甯革細" + "銆愬叕浼楀彿銆戞湭鏌ヨ鍒版偅鑰呮。妗堜俊鎭紝鏃犳硶閫氳繃鍏紬鍙峰彂閫�");
+                return resultMap;
+            }
+
+            // his绯荤粺鐨勬偅鑰卛d鍙栬嚜鎮h�呮。妗坧at_archive.patid_his锛�
+            // service_subtask鏌ヨ鏈叧鑱旇瀛楁锛坰erviceSubtask.getPatidHis()鎭掍负绌猴級锛屼笉鑳界敤浜庢牎楠�
+            if (StringUtils.isEmpty(patArchive.getPatidHis())) {
                 resultMap.put("code", -1);
                 resultMap.put("message", "鍙戦�佸紓甯革細" + "銆愬叕浼楀彿銆戝叕浼楀彿鍙戦�佸け璐�,his绯荤粺鐨勬偅鑰卛d涓虹┖");
                 return resultMap;
diff --git a/ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceTaskScheduleServiceImpl.java b/ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceTaskScheduleServiceImpl.java
index 0af651b..bd327bc 100644
--- a/ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceTaskScheduleServiceImpl.java
+++ b/ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceTaskScheduleServiceImpl.java
@@ -23,6 +23,10 @@
 import com.ruoyi.task.mapper.ServiceSubtaskPreachformMapper;
 import com.ruoyi.task.domain.ServiceTaskScheduleVO;
 import com.ruoyi.task.service.IServiceTaskScheduleService;
+import com.ruoyi.task.domain.ServiceTask;
+import com.ruoyi.task.mapper.ServiceTaskMapper;
+import com.ruoyi.template.domain.SvyTaskTemplateScript;
+import com.ruoyi.template.mapper.SvyTaskTemplateScriptMapper;
 
 /**
  * 闅忚浠诲姟璁″垝 Service 瀹炵幇
@@ -46,6 +50,12 @@
     @Autowired
     private ServiceSubtaskPreachformMapper taskPreachformPort;
 
+    @Autowired
+    private ServiceTaskMapper serviceTaskMapper;
+
+    @Autowired
+    private SvyTaskTemplateScriptMapper svyTaskTemplateScriptMapper;
+
     /** 鍗曟鎵弿鐨勬渶澶ц鍒掓暟锛岄伩鍏嶉暱浜嬪姟銆� */
     private static final int ROLL_BATCH_LIMIT = 200;
 
@@ -57,6 +67,10 @@
         }
         if (CollectionUtils.isEmpty(vo.getDetailList())) {
             throw new BaseException("浠诲姟璁″垝鏄庣粏涓嶈兘涓虹┖锛堣嚦灏戜竴鏉� detail锛�");
+        }
+        ServiceTask serviceTask = serviceTaskMapper.selectServiceTaskByTaskid(vo.getTaskid());
+        if (serviceTask == null) {
+            throw new BaseException("浠诲姟涓嶅瓨鍦ㄦ垨宸插垹闄わ紝taskid=" + vo.getTaskid());
         }
 
         // 1. 鏃ц鍒掑垹
@@ -100,6 +114,10 @@
         for (ServiceTaskScheduleDetail detail : detailList) {
             detail.setTaskid(vo.getTaskid());
             detail.setScheduleid(schedule.getId());
+            Long resolvedTemplateId = resolveTaskTemplateId(serviceTask, detail);
+            if (resolvedTemplateId != null) {
+                detail.setTemplateid(resolvedTemplateId);
+            }
             if (detail.getSeq() == null) {
                 detail.setSeq(seq);
             }
@@ -120,6 +138,48 @@
         log.info("[浠诲姟缁刔 taskid={} 璁″垝淇濆瓨瀹屾垚, scheduleId={}, detail 鏉℃暟={}",
                 vo.getTaskid(), schedule.getId(), detailList.size());
         return schedule;
+    }
+
+    /**
+     * 璁″垝鏄庣粏鐨� templateid 搴斾繚瀛樹换鍔℃ā鏉縄D锛坰vy_task_template.id锛夛紝
+     * libtemplateid 鎵嶄繚瀛樻ā鏉垮簱ID锛坰vy_lib_template_script.svyid锛夈��
+     * 鍏煎鍓嶇浠嶄紶妯℃澘搴揑D鐨勬儏鍐碉細鑻ユ槑缁嗘ā鏉垮湪浠诲姟妯℃澘闂琛ㄤ腑涓嶅瓨鍦紝
+     * 涓旇ID灏辨槸浠诲姟鐨勬ā鏉垮簱ID锛屽垯鍥為��鍒颁换鍔¤嚜宸辩殑妯℃澘ID銆�
+     */
+    private Long resolveTaskTemplateId(ServiceTask serviceTask, ServiceTaskScheduleDetail detail) {
+        Long detailTemplateId = detail.getTemplateid();
+        if (detailTemplateId == null || serviceTask == null) {
+            return detailTemplateId;
+        }
+        if (hasTaskTemplateScripts(detailTemplateId)) {
+            return detailTemplateId;
+        }
+        // 鍙湁闂嵎浠诲姟鎵嶄緷璧� svy_task_template_script锛屽叾瀹冧换鍔$被鍨嬩繚鎸佸師閫昏緫
+        if (!"2".equals(serviceTask.getType())) {
+            return detailTemplateId;
+        }
+
+        Long taskTemplateId = serviceTask.getTemplateid();
+        boolean detailUsesLibTemplate = detailTemplateId.equals(detail.getLibtemplateid())
+                || String.valueOf(detailTemplateId).equals(serviceTask.getLibtemplateid());
+        if (detailUsesLibTemplate && hasTaskTemplateScripts(taskTemplateId)) {
+            log.warn("璁″垝鏄庣粏妯℃澘ID [{}] 鏄ā鏉垮簱ID锛屽凡鏄犲皠涓轰换鍔℃ā鏉縄D [{}], taskid={}",
+                    detailTemplateId, taskTemplateId, serviceTask.getTaskid());
+            return taskTemplateId;
+        }
+        log.warn("璁″垝鏄庣粏妯℃澘ID [{}] 鍦ㄤ换鍔℃ā鏉块棶棰樿〃涓笉瀛樺湪, taskid={}, taskTemplateId={}, taskLibTemplateId={}",
+                detailTemplateId, serviceTask.getTaskid(), taskTemplateId, serviceTask.getLibtemplateid());
+        return detailTemplateId;
+    }
+
+    private boolean hasTaskTemplateScripts(Long templateId) {
+        if (templateId == null) {
+            return false;
+        }
+        SvyTaskTemplateScript query = new SvyTaskTemplateScript();
+        query.setTemplateID(templateId);
+        query.setDelFlag("0");
+        return CollectionUtils.isNotEmpty(svyTaskTemplateScriptMapper.selectSvyTaskTemplateScriptList(query));
     }
 
     @Override
@@ -230,6 +290,7 @@
             log.warn("[浠诲姟缁�-婊氬姩] scheduleId={} 鏄庣粏涓虹┖, 璺宠繃", scheduleId);
             return false;
         }
+        ServiceTask serviceTask = serviceTaskMapper.selectServiceTaskByTaskid(schedule.getTaskid());
 
         // 鎺ㄨ繘姝ラ暱锛堣疆闂撮殧锛�
         int stepNum = schedule.getScheduleTimeNum() == null ? 0 : schedule.getScheduleTimeNum();
@@ -258,7 +319,7 @@
                         scheduleId, src.getScheduleSeq(), src.getId());
                 continue;
             }
-            ServiceSubtask copy = cloneForNextLoop(src, detail, nextLoop, stepNum, stepUnit, now);
+            ServiceSubtask copy = cloneForNextLoop(src, serviceTask, detail, nextLoop, stepNum, stepUnit, now);
             serviceSubtaskMapper.insertServiceSubtask(copy);
             // 澶嶅埗 preachform
             copyPreachform(src.getId(), copy);
@@ -282,7 +343,8 @@
     /**
      * 鍏嬮殕 subtask 鍒颁笅涓�杞細娓呬富閿�佹竻鍥炵瀛楁銆佹帹杩� visit_time銆佽缃� loop_count銆�
      */
-    private ServiceSubtask cloneForNextLoop(ServiceSubtask src, ServiceTaskScheduleDetail detail,
+    private ServiceSubtask cloneForNextLoop(ServiceSubtask src, ServiceTask serviceTask,
+                                            ServiceTaskScheduleDetail detail,
                                             long nextLoop, int stepNum, int stepUnit, Date now) {
         ServiceSubtask copy = JSON.parseObject(JSON.toJSONString(src), ServiceSubtask.class);
         copy.setId(null);
@@ -294,9 +356,10 @@
         copy.setSendstate(2L);
         copy.setLoopCount(nextLoop);
         copy.setScheduleDetailId(detail.getId());
-        // 妯℃澘锛歞etail 浼樺厛
-        if (detail.getTemplateid() != null) {
-            copy.setTemplateid(detail.getTemplateid());
+        // 妯℃澘锛歞etail 浼樺厛锛屽苟鎶婂巻鍙叉暟鎹噷鐨勬ā鏉垮簱ID鏄犲皠涓轰换鍔℃ā鏉縄D
+        Long resolvedTemplateId = resolveTaskTemplateId(serviceTask, detail);
+        if (resolvedTemplateId != null) {
+            copy.setTemplateid(resolvedTemplateId);
             copy.setTemplatename(detail.getTemplatename());
         }
         if (detail.getLibtemplateid() != null) {
diff --git a/ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceTaskServiceImpl.java b/ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceTaskServiceImpl.java
index 4e7a85d..507d506 100644
--- a/ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceTaskServiceImpl.java
+++ b/ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceTaskServiceImpl.java
@@ -482,33 +482,50 @@
             serviceSubtaskVO.setPatid(patid);
         }
         List<ServiceSubtask> serviceSubtaskList = serviceSubtaskMapper.selectServiceSubtaskList(serviceSubtaskVO);
-        List<SvyTaskTemplateScriptVO> svyTaskTemplateScriptVOS = null;
-        if (CollectionUtils.isNotEmpty(serviceSubtaskList) && serviceSubtaskList.size() == 1) {
-            if (serviceSubtaskList.get(0).getSendstate() == 3 && isFinish == true) {
-                ServiceSubtask serviceSubtask = new ServiceSubtask();
-                serviceSubtask.setSendstate(1L);
-               serviceSubtask.setId(serviceSubtaskList.get(0).getId());
-                serviceSubtaskMapper.updateServiceSubtaskByPatId(serviceSubtask);
-            }
-
-            //閫氳繃妯℃澘ID鑾峰彇闂淇℃伅
-            SvyTaskTemplateScript svyTaskTemplateScript = new SvyTaskTemplateScript();
-            svyTaskTemplateScript.setTemplateID(Long.valueOf(serviceTask.getTemplateid()));
-            if (serviceSubtaskList.get(0).getTemplateid() != null)
-                svyTaskTemplateScript.setTemplateID(serviceSubtaskList.get(0).getTemplateid());
-            else if (serviceTask.getTemplateid() != null)
-                svyTaskTemplateScript.setTemplateID(serviceTask.getTemplateid());
-            svyTaskTemplateScript.setDelFlag("0");
-            List<SvyTaskTemplateScript> svyTaskTemplateScripts = svyTaskTemplateScriptMapper.selectSvyTaskTemplateScriptList(svyTaskTemplateScript);
-            if (CollectionUtils.isEmpty(svyTaskTemplateScripts) || svyTaskTemplateScripts.size() == 0) {
-                insertSvyTaskTempScript(null, serviceTask);
-                svyTaskTemplateScripts = svyTaskTemplateScriptMapper.selectSvyTaskTemplateScriptList(svyTaskTemplateScript);
-                if (CollectionUtils.isEmpty(svyTaskTemplateScripts)) {
-                    throw new BaseException("璇疯仈绯荤鐞嗗憳锛屾鏌ラ棶棰樻ā鏉夸俊鎭�!");
-                }
-            }
-            svyTaskTemplateScriptVOS = DtoConversionUtils.sourceToTarget(svyTaskTemplateScripts, SvyTaskTemplateScriptVO.class);
+        if (CollectionUtils.isEmpty(serviceSubtaskList)) {
+            throw new BaseException("鏈壘鍒版偅鑰呭瓙浠诲姟锛宼askid=" + serviceTask.getTaskid()
+                    + ", patid=" + patid + ", subId=" + subId);
         }
+        if (serviceSubtaskList.size() > 1) {
+            throw new BaseException("瀛樺湪澶氭潯鎮h�呭瓙浠诲姟锛岃浼犲叆瀛愪换鍔D鍚庡啀鏌ヨ锛宼askid=" + serviceTask.getTaskid()
+                    + ", patid=" + patid);
+        }
+
+        ServiceSubtask serviceSubtask = serviceSubtaskList.get(0);
+        if (Long.valueOf(3L).equals(serviceSubtask.getSendstate()) && Boolean.TRUE.equals(isFinish)) {
+            ServiceSubtask updateSubtask = new ServiceSubtask();
+            updateSubtask.setSendstate(1L);
+            updateSubtask.setPatid(patid);
+            updateSubtask.setTaskid(serviceTask.getTaskid());
+            serviceSubtaskMapper.updateServiceSubtaskByPatId(updateSubtask);
+        }
+
+        // 瀛愪换鍔℃ā鏉夸紭鍏堬紱瀛愪换鍔℃湭璁剧疆鏃跺洖閫�鍒颁换鍔℃ā鏉裤��
+        Long templateId = serviceSubtask.getTemplateid() != null
+                ? serviceSubtask.getTemplateid() : serviceTask.getTemplateid();
+        if (templateId == null) {
+            throw new BaseException("浠诲姟鏈厤缃棶鍗锋ā鏉匡紝taskid=" + serviceTask.getTaskid());
+        }
+
+        // 鏌ヨ涓庡厹搴曞鍒跺繀椤讳娇鐢ㄥ悓涓�涓ā鏉縄D锛屽惁鍒欎細鍑虹幇鈥滃鍒跺埌A銆佷粛鏌鈥濈殑姝诲惊鐜��
+        SvyTaskTemplateScript svyTaskTemplateScript = new SvyTaskTemplateScript();
+        svyTaskTemplateScript.setTemplateID(templateId);
+        svyTaskTemplateScript.setDelFlag("0");
+        List<SvyTaskTemplateScript> svyTaskTemplateScripts =
+                svyTaskTemplateScriptMapper.selectSvyTaskTemplateScriptList(svyTaskTemplateScript);
+        if (CollectionUtils.isEmpty(svyTaskTemplateScripts)) {
+            log.warn("闂嵎浠诲姟妯℃澘闂涓虹┖锛屽紑濮嬩粠妯℃澘搴撳厹搴曞鍒�, taskid={}, patid={}, subId={}, queryTemplateId={}, taskTemplateId={}, libTemplateId={}",
+                    serviceTask.getTaskid(), patid, subId, templateId,
+                    serviceTask.getTemplateid(), serviceTask.getLibtemplateid());
+            insertSvyTaskTempScript(null, serviceTask, templateId);
+            svyTaskTemplateScripts = svyTaskTemplateScriptMapper.selectSvyTaskTemplateScriptList(svyTaskTemplateScript);
+            if (CollectionUtils.isEmpty(svyTaskTemplateScripts)) {
+                throw new BaseException("璇疯仈绯荤鐞嗗憳锛屾鏌ラ棶棰樻ā鏉夸俊鎭�! taskid=" + serviceTask.getTaskid()
+                        + ", templateId=" + templateId + ", libTemplateId=" + serviceTask.getLibtemplateid());
+            }
+        }
+        List<SvyTaskTemplateScriptVO> svyTaskTemplateScriptVOS =
+                DtoConversionUtils.sourceToTarget(svyTaskTemplateScripts, SvyTaskTemplateScriptVO.class);
         //鑾峰彇鎮h�呬俊鎭�
         PatArchive patArchive = null;
         if (patfrom.equals("0")) patArchive = taskArchivePort.findByPatid(patid);
@@ -567,7 +584,7 @@
                     svyTaskTemplateScriptVO.setSvyTaskTemplateTargetoptions(svyTaskTemplateTargetoptions);
                 } else {
                     //濡傛灉閫夐」娌℃湁鏌ュ埌锛岄渶瑕佸幓鍩虹妯℃澘搴撳皢閫夐」鏌ヨ鍑烘潵锛屾斁鍒颁换鍔℃ā鏉块�夐」搴撲腑
-                    insertSvyTaskTempScript(svyTaskTemplateScriptVO, serviceTask);
+                    insertSvyTaskTempScript(svyTaskTemplateScriptVO, serviceTask, templateId);
 
                 }
             }
@@ -577,8 +594,13 @@
     }
 
 
-    private void insertSvyTaskTempScript(SvyTaskTemplateScriptVO svyTaskTemplateScriptVO, ServiceTask serviceTask) {
-        log.info("-------鍩虹妯℃澘libtemplateid鐨勫�间负锛歿}", serviceTask.getLibtemplateid());
+    private void insertSvyTaskTempScript(SvyTaskTemplateScriptVO svyTaskTemplateScriptVO,
+                                         ServiceTask serviceTask, Long targetTemplateId) {
+        log.info("-------鍩虹妯℃澘libtemplateid鐨勫�间负锛歿}锛岀洰鏍噒emplateID涓猴細{}", serviceTask.getLibtemplateid(), targetTemplateId);
+        if (targetTemplateId == null) {
+            log.warn("鐩爣妯℃澘ID涓虹┖锛屾棤娉曞鍒跺熀纭�妯℃澘闂, taskid={}", serviceTask.getTaskid());
+            return;
+        }
         if (StringUtils.isEmpty(serviceTask.getLibtemplateid())) {
             return;
         }
@@ -589,7 +611,8 @@
         log.info("-------鍩虹妯℃澘svyLibTemplateScripts闂鍊间负锛歿}", svyLibTemplateScripts.size());
         for (SvyLibTemplateScript sll : svyLibTemplateScripts) {
             if (ObjectUtils.isNotEmpty(svyTaskTemplateScriptVO)) {
-                if (sll.getScriptContent().trim().equals(svyTaskTemplateScriptVO.getScriptContent().trim())) {
+                if (StringUtils.equals(StringUtils.trim(sll.getScriptContent()),
+                        StringUtils.trim(svyTaskTemplateScriptVO.getScriptContent()))) {
                     SvyLibTemplateTargetoption sltto = new SvyLibTemplateTargetoption();
                     sltto.setScriptid(sll.getId());
                     List<SvyLibTemplateTargetoption> svyLibTemplateTargetoptions = svyLibTemplateTargetoptionMapper.selectSvyLibTemplateTargetoptionList(sltto);
@@ -597,7 +620,7 @@
                     //灏嗘煡鍑烘潵鐨勯�夐」锛屾斁鍒皊vyTaskTemplateTargetoption涓�
                     List<SvyTaskTemplateTargetoption> svyTaskTemplateTargetoptionList = DtoConversionUtils.sourceToTarget(svyLibTemplateTargetoptions, SvyTaskTemplateTargetoption.class);
                     for (int i = 0; i < svyTaskTemplateTargetoptionList.size(); i++) {
-                        svyTaskTemplateTargetoptionList.get(i).setTemplateID(svyTaskTemplateScriptVO.getTemplateID());
+                        svyTaskTemplateTargetoptionList.get(i).setTemplateID(targetTemplateId);
                         svyTaskTemplateTargetoptionList.get(i).setScriptid(svyTaskTemplateScriptVO.getId());
                         svyTaskTemplateTargetoptionMapper.insertSvyTaskTemplateTargetoption(svyTaskTemplateTargetoptionList.get(i));
                         log.info("-------鏂板瀹屼箣鍚庣殑閫夐」ID鍊间负锛歿}", svyTaskTemplateTargetoptionList.get(i).getId());
@@ -609,7 +632,7 @@
                 SvyTaskTemplateScript stts = DtoConversionUtils.sourceToTarget(sll, SvyTaskTemplateScript.class);
                 stts.setId(null);
                 stts.setTaskid(serviceTask.getTaskid());
-                stts.setTemplateID(serviceTask.getTemplateid());
+                stts.setTemplateID(targetTemplateId);
                 svyTaskTemplateScriptMapper.insertSvyTaskTemplateScript(stts);
 
                 //鏂板闂閫夐」
@@ -618,7 +641,7 @@
                 List<SvyLibTemplateTargetoption> svyLibTemplateTargetoptions = svyLibTemplateTargetoptionMapper.selectSvyLibTemplateTargetoptionList(sltto);
                 List<SvyTaskTemplateTargetoption> svyTaskTemplateTargetoptionList = DtoConversionUtils.sourceToTarget(svyLibTemplateTargetoptions, SvyTaskTemplateTargetoption.class);
                 for (int i = 0; i < svyTaskTemplateTargetoptionList.size(); i++) {
-                    svyTaskTemplateTargetoptionList.get(i).setTemplateID(serviceTask.getTemplateid());
+                    svyTaskTemplateTargetoptionList.get(i).setTemplateID(targetTemplateId);
                     svyTaskTemplateTargetoptionList.get(i).setScriptid(stts.getId());
                     svyTaskTemplateTargetoptionMapper.insertSvyTaskTemplateTargetoption(svyTaskTemplateTargetoptionList.get(i));
                 }
diff --git a/ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceTaskdeptServiceImpl.java b/ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceTaskdeptServiceImpl.java
index d474c64..732be29 100644
--- a/ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceTaskdeptServiceImpl.java
+++ b/ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceTaskdeptServiceImpl.java
@@ -106,4 +106,9 @@
     public int deleteServiceTaskdeptById(Long id) {
         return serviceTaskdeptMapper.deleteServiceTaskdeptById(id);
     }
+
+    @Override
+    public int deleteServiceTaskdeptByTaskId(ServiceTaskdept serviceTaskdept) {
+        return serviceTaskdeptMapper.deleteServiceTaskdeptByTaskId(serviceTaskdept);
+    }
 }
diff --git a/ruoyi-task/src/main/resources/mapper/task/ServiceSubtaskMapper.xml b/ruoyi-task/src/main/resources/mapper/task/ServiceSubtaskMapper.xml
index ea2743c..c9b4b74 100644
--- a/ruoyi-task/src/main/resources/mapper/task/ServiceSubtaskMapper.xml
+++ b/ruoyi-task/src/main/resources/mapper/task/ServiceSubtaskMapper.xml
@@ -23,6 +23,7 @@
         <result property="senduuid" column="senduuid"/>
         <result property="result" column="result"/>
         <result property="finishtime" column="finishtime"/>
+        <result property="finishState" column="finish_state"/>
         <result property="recordid" column="recordid"/>
         <result property="exrecallcount" column="exrecallcount"/>
         <result property="pulltime" column="pulltime"/>
@@ -77,6 +78,7 @@
         <result property="leavehospitaldistrictname" column="leavehospitaldistrictname"/>
         <result property="excep" column="excep"/>
         <result property="remark" column="remark"/>
+        <result property="doctorOpinion" column="doctor_opinion"/>
         <result property="suggest" column="suggest"/>
         <result property="sendstate" column="sendstate"/>
         <result property="inhospid" column="inhospid"/>
@@ -85,6 +87,7 @@
         <result property="visitCount" column="visit_count"/>
         <result property="isVisitAgain" column="is_visit_again"/>
         <result property="upid" column="upid"/>
+        <result property="visitType" column="visit_type"/>
         <result property="visitTime" column="visit_time"/>
         <result property="visitDeptName" column="visit_dept_name"/>
         <result property="visitDeptCode" column="visit_dept_code"/>
@@ -187,6 +190,7 @@
                long_send_time,
                openid,
                remark,
+               doctor_opinion,
                service_type,
                libtemplateid,
                libtemplatename,
@@ -207,6 +211,7 @@
                senduuid,
                result,
                finishtime,
+               finish_state,
                recordid,
                exrecallcount,
                pulltime,
@@ -258,6 +263,58 @@
                task_situation,
                is_manually_called
         from service_subtask
+    </sql>
+
+    <!-- 瓒呮椂瀹屾垚銆佸欢鏈熷畬鎴愰�昏緫 瀹屾垚鏃剁姸鎬�(finish_state)璁$畻锛氫粎褰撴湰娆℃洿鏂版妸 sendstate 缃负 6锛堥殢璁垮畬鎴愶級鏃朵娇鐢ㄣ��
+         鍩哄噯鎴鏃堕棿 base = endtime + send_day(缂虹渷1) + (危 service_subtask_preachform.compensate_time + 1) 澶╋紝
+         涓� RyTask/RedisMqReceiver 鐨� isSubtaskDeadline 鍙e緞淇濇寔涓�鑷达紱
+         delay = COALESCE(service_task.delay_finish_days, 0)锛�
+         finish_time = COALESCE(鍏ュ弬 finishtime, 搴撲腑 finishtime, NOW())锛�
+           finish_time <= base                                    -> 1 姝e父瀹屾垚
+           delay > 0 涓� finish_time <= base + delay               -> 2 寤舵湡瀹屾垚
+           鍏朵綑锛堝惈 delay 涓� 0 鎴� NULL銆佹垨宸茶秴鍑哄欢鏈熺獥鍙o級          -> 3 瓒呮椂瀹屾垚
+         finishtime 涓虹┖鏃剁敤 NOW() 鍏滃簳锛屼繚璇佸畬鎴愭椂涓�瀹氬啓鍏ョ姸鎬侊紱
+         浠呭綋 endtime 涓虹┖瀵艰嚧鏃犳硶璁$畻鏃舵墠淇濇寔鍘熷�间笉瑕嗙洊銆� -->
+    <sql id="finishStateExpr">
+        CASE
+        WHEN service_subtask.endtime IS NULL THEN finish_state
+        WHEN COALESCE(#{finishtime,jdbcType=TIMESTAMP}, service_subtask.finishtime, NOW())
+        &lt;= DATE_ADD(
+        DATE_ADD(
+        service_subtask.endtime,
+        INTERVAL COALESCE((SELECT st.send_day FROM service_task st
+        WHERE st.taskid = service_subtask.taskid AND st.del_flag = '0' LIMIT 1), 1) DAY
+        ),
+        INTERVAL (COALESCE((SELECT SUM(CAST(COALESCE(NULLIF(TRIM(pf.compensate_time), ''), '0') AS SIGNED))
+        FROM service_subtask_preachform pf
+        WHERE pf.subid = service_subtask.id AND pf.del_flag = 0), 0) + 1) DAY
+        )
+        THEN 1
+        WHEN COALESCE((SELECT st.delay_finish_days FROM service_task st
+        WHERE st.taskid = service_subtask.taskid AND st.del_flag = '0' LIMIT 1), 0) &gt; 0
+        AND COALESCE(#{finishtime,jdbcType=TIMESTAMP}, service_subtask.finishtime, NOW())
+        &lt;= DATE_ADD(
+        DATE_ADD(
+        DATE_ADD(
+        service_subtask.endtime,
+        INTERVAL COALESCE((SELECT st.send_day FROM service_task st
+        WHERE st.taskid = service_subtask.taskid AND st.del_flag = '0' LIMIT 1), 1) DAY
+        ),
+        INTERVAL (COALESCE((SELECT SUM(CAST(COALESCE(NULLIF(TRIM(pf.compensate_time), ''), '0') AS SIGNED))
+        FROM service_subtask_preachform pf
+        WHERE pf.subid = service_subtask.id AND pf.del_flag = 0), 0) + 1) DAY
+        ),
+        INTERVAL COALESCE((SELECT st.delay_finish_days FROM service_task st
+        WHERE st.taskid = service_subtask.taskid AND st.del_flag = '0' LIMIT 1), 0) DAY
+        )
+        THEN 2
+        ELSE 3
+        END
+    </sql>
+
+    <!-- 褰撳墠鎵ц鏂瑰紡锛氶娆¢殢璁� current_preachform 涓虹┖鏃讹紝鍙� preachform 鐨勭涓�涓墽琛屾柟寮忓厹搴� -->
+    <sql id="effectiveCurrentPreachformExpr">
+        COALESCE(NULLIF(TRIM(current_preachform), ''), TRIM(SUBSTRING_INDEX(preachform, ',', 1)))
     </sql>
 
     <select id="selectServiceSubtaskBySendstate" parameterType="com.ruoyi.task.domain.entity.ServiceSubtaskEntity"
@@ -325,6 +382,37 @@
             <foreach collection="leaveldeptcodes" item="lvDeptCodeItem" open="(" separator="," close=")">
                 #{lvDeptCodeItem}
             </foreach>
+        </if>
+    </sql>
+
+    <!-- 鍗冲皢瓒呮椂杩囨护锛氭槸鍚﹀嵆灏嗚秴鏃�(soonTimeout=1) + 鍗冲皢瓒呮椂鏃堕棿(soonTimeoutHours锛屽崟浣嶅皬鏃�)銆�
+         鎴鏃堕棿鍙e緞涓� RyTask/RedisMqReceiver 鐨� isSubtaskDeadline 淇濇寔涓�鑷达細
+         service_subtask.endtime + service_task.send_day(缂虹渷1) + (危 service_subtask_preachform.compensate_time + 1) 澶╋紱
+         浠呭綋瀛樺湪鏈夋晥琛ュ伩璁板綍鏃舵墠璁$畻鎴鏃堕棿锛堟棤琛ュ伩璁板綍涓嶅弬涓庡嵆灏嗚秴鏃讹級锛�
+         浠呯粺璁♀�滃皻鏈秴鏃躲�佷笖鍦� N 灏忔椂鍐呭皢瑕佽秴鏃垛�濈殑鏁版嵁銆� -->
+    <sql id="soonTimeoutDeadlineExpr">
+        (
+        SELECT DATE_ADD(
+        DATE_ADD(ss.endtime, INTERVAL COALESCE(st.send_day, 1) DAY),
+        INTERVAL (COALESCE(pf.compensate_days, 0) + 1) DAY
+        )
+        FROM service_subtask ss
+        LEFT JOIN service_task st ON st.taskid = ss.taskid AND st.del_flag = '0'
+        INNER JOIN (
+        SELECT subid,
+        SUM(CAST(COALESCE(NULLIF(TRIM(compensate_time), ''), '0') AS SIGNED)) AS compensate_days
+        FROM service_subtask_preachform
+        WHERE del_flag = 0
+        GROUP BY subid
+        ) pf ON pf.subid = ss.id
+        WHERE ss.id = service_subtask.id
+        )
+    </sql>
+
+    <sql id="soonTimeoutFilter">
+        <if test="soonTimeout != null and soonTimeout == 1">
+            AND <include refid="soonTimeoutDeadlineExpr"/> &gt; NOW()
+            AND <include refid="soonTimeoutDeadlineExpr"/> &lt;= DATE_ADD(NOW(), INTERVAL COALESCE(#{soonTimeoutHours}, 0) HOUR)
         </if>
     </sql>
 
@@ -572,7 +660,10 @@
         <if test="campusid != null  and campusid != ''">and campusid = #{campusid}</if>
         <if test="pid != null ">and pid = #{pid}</if>
         <if test="preachform != null ">and preachform = #{preachform}</if>
-        <if test="currentPreachform != null ">and current_preachform = #{currentPreachform}</if>
+        <if test="currentPreachform != null and currentPreachform != '' ">and current_preachform = #{currentPreachform}</if>
+        <if test="currentPreachforms != null and currentPreachforms != ''">
+            and FIND_IN_SET(<include refid="effectiveCurrentPreachformExpr"/>, #{currentPreachforms})
+        </if>
         <if test="guid != null  and guid != ''">and guid = #{guid}</if>
         <if test="textParam != null  and textParam != ''">and text_param = #{textParam}</if>
         <if test="bedNo != null">and bed_no = #{bedNo}</if>
@@ -602,6 +693,7 @@
             AND visit_count > 1
         </if>
         <if test="remark != null">and remark = #{remark}</if>
+        <if test="doctorOpinion != null and doctorOpinion != ''">and doctor_opinion like concat('%', #{doctorOpinion}, '%')</if>
         <if test="suggest != null">and suggest = #{suggest}</if>
         <if test="createBy != null">and create_by = #{createBy}</if>
         <if test="taskGuid != null">and task_guid = #{taskGuid}</if>
@@ -616,6 +708,7 @@
                 #{visitDeptCode}
             </foreach>
         </if>
+        <include refid="soonTimeoutFilter"/>
     </sql>
 
     <!-- 鎺掑簭鐗囨锛氫繚鎸佸師璇箟锛屽彧鍦ㄨ皟鐢ㄦ柟鏄惧紡浼� sort 鏃舵墠鎷� order by
@@ -716,7 +809,7 @@
         <if test="visitDeptName != null">and visit_dept_name = #{visitDeptName}</if>
         <if test="managementDoctorCode != null">and management_doctor_code = #{managementDoctorCode}</if>
         <if test="managementDoctor != null">and management_doctor = #{managementDoctor}</if>
-        <if test="currentPreachform != null">and current_preachform = #{currentPreachform}</if>
+        <if test="currentPreachform != null and currentPreachform != '' ">and current_preachform = #{currentPreachform}</if>
         <if test="isabnormal != null">and isabnormal = #{isabnormal}</if>
         <if test="continueFlag != null ">and continue_flag = #{continueFlag}</if>
         <if test="continueTimeNow != null ">and continue_time_now = #{continueTimeNow,jdbcType=TIMESTAMP}</if>
@@ -775,7 +868,7 @@
         <if test="visitTime != null ">and visit_time = #{visitTime}</if>
         <if test="visitDeptCode != null">and visit_dept_code = #{visitDeptCode}</if>
         <if test="visitDeptName != null">and visit_dept_name = #{visitDeptName}</if>
-        <if test="currentPreachform != null">and current_preachform = #{currentPreachform}</if>
+        <if test="currentPreachform != null and currentPreachform != '' ">and current_preachform = #{currentPreachform}</if>
         <if test="managementDoctorCode != null">and management_doctor_code = #{managementDoctorCode}</if>
         <if test="isabnormal != null">and isabnormal = #{isabnormal}</if>
         <if test="managementDoctor != null">and management_doctor like concat('%',#{managementDoctor}, '%')</if>
@@ -783,6 +876,7 @@
         <if test="continueTimeNow != null ">and continue_time_now = #{continueTimeNow,jdbcType=TIMESTAMP}</if>
         <if test="continueCount != null ">and continue_count = #{continueCount}</if>
         <if test="continueTimeNext != null ">and continue_time_next = #{continueTimeNext}</if>
+        <include refid="soonTimeoutFilter"/>
     </select>
 
     <select id="selectSendstateByCondition" parameterType="com.ruoyi.task.domain.ServiceSubtask" resultType="Integer">
@@ -949,6 +1043,12 @@
         </if>
         <if test="managementDoctorCode != null">and management_doctor_code = #{managementDoctorCode}</if>
         <if test="managementDoctor != null">and management_doctor like concat('%',#{managementDoctor}, '%')</if>
+        <if test="startOutHospTime != null">
+            AND date_format(endtime,'%y%m%d') &gt;= date_format(#{startOutHospTime},'%y%m%d')
+        </if>
+        <if test="endOutHospTime != null">
+            AND date_format(endtime,'%y%m%d') &lt;= date_format(#{endOutHospTime},'%y%m%d')
+        </if>
         ) a where a.finished =1)
     </select>
 
@@ -1002,6 +1102,7 @@
             <if test="senduuid != null">senduuid,</if>
             <if test="result != null">result,</if>
             <if test="finishtime != null">finishtime,</if>
+            <if test="finishState != null">finish_state,</if>
             <if test="recordid != null">recordid,</if>
             <if test="exrecallcount != null">exrecallcount,</if>
             <if test="pulltime != null">pulltime,</if>
@@ -1064,6 +1165,7 @@
             <if test="nurseName != null ">nurse_name,</if>
             <if test="nurseId != null ">nurse_id,</if>
             <if test="remark != null ">remark,</if>
+            <if test="doctorOpinion != null ">doctor_opinion,</if>
             <if test="suggest != null ">suggest,</if>
             <if test="sendstate != null ">sendstate,</if>
             <if test="inhospid != null ">inhospid,</if>
@@ -1078,7 +1180,7 @@
             <if test="visitDeptName != null">visit_dept_name,</if>
             <if test="managementDoctorCode != null">management_doctor_code,</if>
             <if test="managementDoctor != null">management_doctor,</if>
-            <if test="currentPreachform != null">current_preachform,</if>
+            <if test="currentPreachform != null and currentPreachform != '' ">current_preachform,</if>
             <if test="taskSituation != null">task_situation,</if>
             <if test="isabnormal != null">isabnormal,</if>
             <if test="continueFlag != null ">continue_flag,</if>
@@ -1109,6 +1211,7 @@
             <if test="senduuid != null">#{senduuid},</if>
             <if test="result != null">#{result},</if>
             <if test="finishtime != null">#{finishtime},</if>
+            <if test="finishState != null">#{finishState},</if>
             <if test="recordid != null">#{recordid},</if>
             <if test="exrecallcount != null">#{exrecallcount},</if>
             <if test="pulltime != null">#{pulltime},</if>
@@ -1168,6 +1271,7 @@
             <if test="nurseName != null ">#{nurseName},</if>
             <if test="nurseId != null ">#{nurseId},</if>
             <if test="remark != null ">#{remark},</if>
+            <if test="doctorOpinion != null ">#{doctorOpinion},</if>
             <if test="suggest != null ">#{suggest},</if>
             <if test="sendstate != null ">#{sendstate},</if>
             <if test="inhospid != null ">#{inhospid},</if>
@@ -1182,7 +1286,7 @@
             <if test="visitDeptName != null">#{visitDeptName},</if>
             <if test="managementDoctorCode != null">#{managementDoctorCode},</if>
             <if test="managementDoctor != null">#{managementDoctor},</if>
-            <if test="currentPreachform != null">#{currentPreachform},</if>
+            <if test="currentPreachform != null and currentPreachform != '' ">#{currentPreachform},</if>
             <if test="taskSituation != null">#{taskSituation},</if>
             <if test="isabnormal != null">#{isabnormal},</if>
             <if test="continueFlag != null ">#{continueFlag},</if>
@@ -1198,6 +1302,15 @@
             <if test="isManuallyCalled != null ">#{isManuallyCalled},</if>
         </trim>
     </insert>
+
+    <!-- 浜屾闅忚骞傜瓑锛氫粎褰� is_visit_again 涓嶆槸鐩爣鍊兼椂鏇存柊锛岃繑鍥炲彈褰卞搷琛屾暟 -->
+    <update id="updateIsVisitAgainById">
+        update service_subtask
+        set is_visit_again = #{isVisitAgain},
+            update_time = now()
+        where id = #{id}
+          and (is_visit_again is null or is_visit_again &lt;&gt; #{isVisitAgain})
+    </update>
 
     <update id="updateServiceSubtask" parameterType="com.ruoyi.task.domain.ServiceSubtask">
         update service_subtask
@@ -1255,6 +1368,7 @@
             <if test="openid != null ">openid = #{openid},</if>
             <if test="longSendTime != null ">long_send_time = #{longSendTime},</if>
             <if test="sendstate != null ">sendstate = #{sendstate},</if>
+            <if test="sendstate != null and sendstate == 6">finish_state = <include refid="finishStateExpr"/>,</if>
             <if test="serviceType != null ">service_type = #{serviceType},</if>
             <if test="score != null ">score = #{score},</if>
             <if test="submit != null ">submit = #{submit},</if>
@@ -1272,6 +1386,7 @@
             <if test="nurseName != null ">nurse_name=#{nurseName},</if>
             <if test="nurseId != null ">nurse_id=#{nurseId},</if>
             <if test="remark != null ">remark=#{remark},</if>
+            <if test="doctorOpinion != null ">doctor_opinion=#{doctorOpinion},</if>
             <if test="suggest != null ">suggest=#{suggest},</if>
             <if test="inhospid != null ">inhospid=#{inhospid},</if>
             <if test="patfrom != null ">patfrom=#{patfrom},</if>
@@ -1285,7 +1400,7 @@
             <if test="visitDeptName != null">visit_dept_name = #{visitDeptName},</if>
             <if test="managementDoctorCode != null">management_doctor_code = #{managementDoctorCode},</if>
             <if test="managementDoctor != null">management_doctor=#{managementDoctor},</if>
-            <if test="currentPreachform != null">current_preachform=#{currentPreachform},</if>
+            <if test="currentPreachform != null and currentPreachform != '' ">current_preachform=#{currentPreachform},</if>
             <if test="taskSituation != null">task_situation=#{taskSituation},</if>
             <if test="isabnormal != null">isabnormal=#{isabnormal},</if>
             <if test="continueFlag != null ">continue_flag = #{continueFlag},</if>
@@ -1359,6 +1474,7 @@
             <if test="openid != null ">openid = #{openid},</if>
             <if test="longSendTime != null ">long_send_time = #{longSendTime},</if>
             <if test="sendstate != null ">sendstate = #{sendstate},</if>
+            <if test="sendstate != null and sendstate == 6">finish_state = <include refid="finishStateExpr"/>,</if>
             <if test="serviceType != null ">service_type = #{serviceType},</if>
             <if test="score != null ">score = #{score},</if>
             <if test="visitCount != null ">visit_count = #{visitCount},</if>
@@ -1377,6 +1493,7 @@
             <if test="nurseName != null ">nurse_name=#{nurseName},</if>
             <if test="nurseId != null ">nurse_id=#{nurseId},</if>
             <if test="remark != null ">remark=#{remark},</if>
+            <if test="doctorOpinion != null ">doctor_opinion=#{doctorOpinion},</if>
             <if test="suggest != null ">suggest=#{suggest},</if>
             <if test="inhospid != null ">inhospid=#{inhospid},</if>
             <if test="patfrom != null ">patfrom=#{patfrom},</if>
@@ -1389,7 +1506,7 @@
             <if test="visitDeptName != null">visit_dept_name = #{visitDeptName},</if>
             <if test="managementDoctorCode != null">management_doctor_code = #{managementDoctorCode},</if>
             <if test="managementDoctor != null">management_doctor=#{managementDoctor},</if>
-            <if test="currentPreachform != null">current_preachform=#{currentPreachform},</if>
+            <if test="currentPreachform != null and currentPreachform != '' ">current_preachform=#{currentPreachform},</if>
             <if test="taskSituation != null">task_situation=#{taskSituation},</if>
             <if test="isabnormal != null">isabnormal=#{isabnormal},</if>
             <if test="continueFlag != null ">continue_flag = #{continueFlag},</if>
@@ -1415,6 +1532,7 @@
             <if test="sex != null">sex = #{sex},</if>
             <if test="delFlag != null">del_flag = #{delFlag},</if>
             <if test="sendstate != null ">sendstate = #{sendstate},</if>
+            <if test="sendstate != null and sendstate == 6">finish_state = <include refid="finishStateExpr"/>,</if>
             <if test="age != null">age = #{age},</if>
             <if test="sfzh != null">sfzh = #{sfzh},</if>
             <if test="addr != null">addr = #{addr},</if>
@@ -1482,6 +1600,7 @@
             <if test="nurseName != null ">nurse_name=#{nurseName},</if>
             <if test="nurseId != null ">nurse_id=#{nurseId},</if>
             <if test="remark != null ">remark=#{remark},</if>
+            <if test="doctorOpinion != null ">doctor_opinion=#{doctorOpinion},</if>
             <if test="suggest != null ">suggest=#{suggest},</if>
             <if test="inhospid != null ">inhospid=#{inhospid},</if>
             <if test="patfrom != null ">patfrom=#{patfrom},</if>
@@ -1493,7 +1612,7 @@
             <if test="visitDeptName != null">visit_dept_name = #{visitDeptName},</if>
             <if test="managementDoctorCode != null">management_doctor_code = #{managementDoctorCode},</if>
             <if test="managementDoctor != null">management_doctor=#{managementDoctor},</if>
-            <if test="currentPreachform != null">current_preachform=#{currentPreachform},</if>
+            <if test="currentPreachform != null and currentPreachform != '' ">current_preachform=#{currentPreachform},</if>
             <if test="taskSituation != null">task_situation=#{taskSituation},</if>
             <if test="isabnormal != null">isabnormal=#{isabnormal},</if>
             <if test="continueFlag != null ">continue_flag = #{continueFlag},</if>
@@ -1526,6 +1645,7 @@
             <if test="phone != null">phone = #{phone},</if>
             <if test="sex != null">sex = #{sex},</if>
             <if test="sendstate != null ">sendstate = #{sendstate},</if>
+            <if test="sendstate != null and sendstate == 6">finish_state = <include refid="finishStateExpr"/>,</if>
             <if test="age != null">age = #{age},</if>
             <if test="sfzh != null">sfzh = #{sfzh},</if>
             <if test="addr != null">addr = #{addr},</if>
@@ -1593,6 +1713,7 @@
             <if test="nurseName != null ">nurse_name=#{nurseName},</if>
             <if test="nurseId != null ">nurse_id=#{nurseId},</if>
             <if test="remark != null ">remark=#{remark},</if>
+            <if test="doctorOpinion != null ">doctor_opinion=#{doctorOpinion},</if>
             <if test="suggest != null ">suggest=#{suggest},</if>
             <if test="inhospid != null ">inhospid=#{inhospid},</if>
             <if test="patfrom != null ">patfrom=#{patfrom},</if>
@@ -1604,7 +1725,7 @@
             <if test="visitDeptName != null">visit_dept_name = #{visitDeptName},</if>
             <if test="managementDoctorCode != null">management_doctor_code = #{managementDoctorCode},</if>
             <if test="managementDoctor != null">management_doctor=#{managementDoctor},</if>
-            <if test="currentPreachform != null">current_preachform=#{currentPreachform},</if>
+            <if test="currentPreachform != null and currentPreachform != '' ">current_preachform=#{currentPreachform},</if>
             <if test="taskSituation != null">task_situation=#{taskSituation},</if>
             <if test="isabnormal != null">isabnormal=#{isabnormal},</if>
             <if test="continueFlag != null ">continue_flag = #{continueFlag},</if>
@@ -1633,6 +1754,35 @@
         where patid = #{patid} and taskid = #{taskid}
     </update>
 
+    <!-- 浜哄伐闅忚锛氳蒋鍒犻櫎鎮h�呭嚭闄㈣褰曞搴旂殑鏈畬鎴愰殢璁夸换鍔� -->
+    <update id="deleteUnfinishedServiceSubtaskByInhospids">
+        update service_subtask
+        set del_flag = 1
+        where del_flag = 0
+          and patid = #{patid}
+          and sendstate in
+          <foreach collection="sendstates" item="sendstate" open="(" separator="," close=")">
+              #{sendstate}
+          </foreach>
+          and inhospid in
+          <foreach collection="inhospids" item="inhospid" open="(" separator="," close=")">
+              #{inhospid}
+          </foreach>
+    </update>
+
+    <!-- 浜哄伐闅忚閲嶅鏍¢獙锛氬悓涓�鎮h�呭悓涓�浠诲姟涓嬫槸鍚﹀凡鏈夋湭瀹屾垚闅忚浠诲姟 -->
+    <select id="countUnfinishedServiceSubtaskByPatidAndTaskid" resultType="int">
+        select count(1)
+        from service_subtask
+        where del_flag = 0
+          and patid = #{patid}
+          and taskid = #{taskid}
+          and sendstate in
+          <foreach collection="sendstates" item="sendstate" open="(" separator="," close=")">
+              #{sendstate}
+          </foreach>
+    </select>
+
     <select id="getSfStatistics" parameterType="com.ruoyi.task.domain.ServiceSubtaskCountReq"
             resultMap="ServiceSubtaskResult">
         select id,
@@ -1650,13 +1800,14 @@
         leaveldeptname,
         sendstate,
         preachform,
-        current_preachform,
+        <include refid="effectiveCurrentPreachformExpr"/> AS current_preachform,
         excep,
         nurse_id,
         nurse_name,
         taskid,
         task_name,
         visit_time,
+        visit_dept_code,
         finishtime,
         endtime,
         senddate,
@@ -1674,7 +1825,9 @@
         continue_content,
         continue_count,
         continue_time_next,
-        task_situation
+        task_situation,
+        finish_state,
+        is_manually_called
         from service_subtask
         where 1=1
         and del_flag = 0
@@ -2385,13 +2538,15 @@
         leaveldeptname,
         sendstate,
         preachform,
-        current_preachform,
+        <include refid="effectiveCurrentPreachformExpr"/> AS current_preachform,
         excep,
         nurse_id,
         nurse_name,
         taskid,
         task_name,
         visit_time,
+        visit_dept_code,
+        visit_dept_name,
         finishtime,
         endtime,
         senddate,
@@ -2410,7 +2565,10 @@
         continue_count,
         continue_time_next,
         task_situation,
-        remark
+        remark,
+        doctor_opinion,
+        finish_state,
+        is_manually_called
         from service_subtask
         where 1=1
         and del_flag = 0
@@ -2455,6 +2613,10 @@
             AND IFNULL(visit_time, IFNULL(finishtime, create_time)) >= DATE(#{startTime})
             AND IFNULL(visit_time, IFNULL(finishtime, create_time)) &lt; DATE_ADD(DATE(#{endTime}), INTERVAL 1 DAY)
         </if>
+        <if test="hospitalStartTime != null and hospitalEndTime!=null">
+            AND endtime >= DATE(#{hospitalStartTime})
+            AND endtime &lt; DATE_ADD(DATE(#{hospitalEndTime}), INTERVAL 1 DAY)
+        </if>
         <if test="visitDeptCode != null">and visit_dept_code = #{visitDeptCode}</if>
         <if test="visitDeptName != null">and visit_dept_name = #{visitDeptName}</if>
         <if test="isabnormal != null">and isabnormal = #{isabnormal}</if>
@@ -2466,10 +2628,15 @@
         <if test="type != null">
             and type = #{type}
         </if>
-        <if test="visitCount != null and visitCount == 1">
+        <!-- 棣栦釜鍐嶆闅忚锛氱簿纭煡 visit_count = 2锛屼笌缁熻鍙e緞涓�鑷� -->
+        <if test="hyperLinkInfoType == 'againFollowUpPushCountInfo'">
+            AND visit_count = 2
+        </if>
+        <!-- 鍏朵粬绫诲瀷淇濇寔鍘熻涔夛細1 棣栨锛�>1 鍐嶆 -->
+        <if test="hyperLinkInfoType != 'againFollowUpPushCountInfo' and visitCount != null and visitCount == 1">
             AND visit_count = 1
         </if>
-        <if test="visitCount != null and visitCount > 1">
+        <if test="hyperLinkInfoType != 'againFollowUpPushCountInfo' and visitCount != null and visitCount > 1">
             AND visit_count > 1
         </if>
         <if test="groupKey != null and groupKey != '' and groupKeyList != null and groupKeyList.size>0">
@@ -2486,8 +2653,8 @@
                 #{state}
             </foreach>
         </if>
-        <if test="currentPreachform != null">
-            AND current_preachform = #{currentPreachform}
+        <if test="currentPreachform != null and currentPreachform != '' ">
+            AND <include refid="effectiveCurrentPreachformExpr"/> = #{currentPreachform}
             <if test="followUpCountStyle != null and followUpCountStyle = '1'">
                 AND sendstate != 4
             </if>
@@ -2498,6 +2665,9 @@
         <if test="taskSituation != null">
             AND task_situation = #{taskSituation}
         </if>
+        <if test="finishState != null">
+            AND finish_state = #{finishState}
+        </if>
         <if test="drcode != null and drcode != ''">
             AND drcode = #{drcode}
         </if>
@@ -2506,6 +2676,12 @@
         </if>
         <if test="deptcode != null and deptcode != ''">
             AND leaveldeptcode = #{deptcode}
+        </if>
+        <if test="isManuallyCalled != null">
+            AND is_manually_called = #{isManuallyCalled}
+        </if>
+        <if test="suggest != null and suggest != ''">
+            AND TRIM(suggest) = #{suggest}
         </if>
         order by endtime desc
         <if test="pageSize != null  and pageNum != null">
@@ -2729,14 +2905,14 @@
     <select id="getSfStatisticsScriptDetails" parameterType="com.ruoyi.task.domain.ServiceSubtaskCountReq"
             resultMap="ServiceSubtaskScriptCountResult">
         SELECT
-        a.id as scriptid,
-        a.templateid as templateid,
-        a.script_type,
+        MIN(a.id) as scriptid,
+        MIN(a.templateid) as templateid,
+        MIN(a.script_type) as script_type,
         a.script_content,
         b.subtask_type,
-        b.completed_quantity,
-        b.all_quantity,
-        ROUND(b.completed_quantity/b.all_quantity,2) as completed_percentage
+        SUM(b.completed_quantity) as completed_quantity,
+        SUM(b.all_quantity) as all_quantity,
+        ROUND(SUM(b.completed_quantity)/SUM(b.all_quantity),2) as completed_percentage
         FROM
         <if test="type == 1">
             ivr_task_template_script a
@@ -2827,25 +3003,37 @@
         group by ssd.scriptid) b
         ON a.id = b.scriptid
         <!-- 閫傜敤鍗曟潯鏌ヨ -->
-        <if test="scriptids != null">
+        <if test="scriptids != null and scriptids != ''">
             AND a.scriptid in
             (SELECT lib_script.scriptid FROM
             svy_lib_template_script lib_script
             WHERE lib_script.scriptid in (${scriptids}))
         </if>
+        <!-- 鍚屼竴閬撻浼氳澶嶅埗鍒板涓换鍔℃ā鏉匡紝鎸夐鐩唴瀹瑰悎骞讹紝淇濊瘉涓�閬撻鍙繑鍥炰竴鏉� -->
+        GROUP BY a.script_content, b.subtask_type
+        ORDER BY MIN(a.id)
     </select>
 
     <select id="getSfStatisticsScriptItemDetails" parameterType="com.ruoyi.task.domain.ServiceSubtaskCountReq"
             resultMap="ServiceSubtaskScriptCountResult">
         SELECT
-        ssd.scriptid,
+        a.script_content,
+        ss.type as subtask_type,
         IFNULL(ssd.matchedtext,ssd.asrtext) AS option_text,
         COUNT(*) AS chosen_quantity
         FROM
         service_subtask_detail ssd
         INNER JOIN service_subtask ss ON ssd.sub_id = ss.id
+        INNER JOIN
+        <if test="type == 1">
+            ivr_task_template_script a ON a.id = ssd.scriptid
+        </if>
+        <if test="type == 2">
+            svy_task_template_script a ON a.id = ssd.scriptid
+        </if>
         where 1=1
         and ss.del_flag=0
+        and ssd.del_flag=0
         <if test="orgid != null and orgid != ''">
             and ss.orgid = #{orgid}
         </if>
@@ -2912,7 +3100,7 @@
         <if test="configValue != null and configValue != ''">
             AND ssd.categoryid IN (${configValue})
         </if>
-        group by ssd.scriptid, IFNULL(ssd.matchedtext,ssd.asrtext)
+        group by a.script_content, ss.type, IFNULL(ssd.matchedtext,ssd.asrtext)
     </select>
 
     <select id="getDataByTime" resultMap="ServiceSubtaskResult">
@@ -2983,6 +3171,7 @@
                nurse_id,
                nurse_name,
                remark,
+               doctor_opinion,
                inhospid,
                patfrom,
                task_guid,
@@ -3230,6 +3419,7 @@
         SUM(CASE WHEN sendstate != 2 THEN 1 ELSE 0 END) AS yfs,
         SUM(CASE WHEN sendstate = 2 THEN 1 ELSE 0 END) AS wfs,
         SUM(CASE WHEN sendstate = 6 THEN 1 ELSE 0 END) AS ywc,
+        SUM(CASE WHEN sendstate = 7 THEN 1 ELSE 0 END) AS cs,
         SUM(CASE WHEN excep IS NOT NULL AND excep = '1' THEN 1 ELSE 0 END) AS yc,
         SUM(CASE WHEN excep IS NOT NULL AND excep = '2' THEN 1 ELSE 0 END) AS jg,
         COUNT(1) as total
@@ -3362,6 +3552,9 @@
                 #{state}
             </foreach>
         </if>
+        <if test="currentPreachforms != null and currentPreachforms != ''">
+            AND FIND_IN_SET(<include refid="effectiveCurrentPreachformExpr"/>, #{currentPreachforms})
+        </if>
         <if test="orgid != null and orgid != ''">
             AND orgid = #{orgid}
         </if>
@@ -3468,32 +3661,74 @@
         COUNT(CASE WHEN sendstate = 5 THEN 1 END) AS failedVisitCount,
         SUM(CASE WHEN excep IS NOT NULL AND excep = '1' THEN 1 ELSE 0 END) AS abnormalVisitCount,
         SUM(CASE WHEN excep IS NOT NULL AND excep = '2' THEN 1 ELSE 0 END) AS warnningVisitCount,
-        COUNT(*) AS allVisitCount
-        FROM service_subtask
-        WHERE del_flag = '0'
-        AND service_type = '2'
-        AND visit_count = 1
+        COUNT(*) AS allVisitCount,
+        <choose>
+            <when test="soonTimeoutHours != null and soonTimeoutHours > 0">
+                COUNT(CASE WHEN t.soon_deadline &gt; NOW()
+                AND t.soon_deadline &lt;= DATE_ADD(NOW(), INTERVAL #{soonTimeoutHours} HOUR)
+                THEN 1 END) AS soonTimeoutCount
+            </when>
+            <otherwise>
+                0 AS soonTimeoutCount
+            </otherwise>
+        </choose>
+        FROM (
+        SELECT ss.*,
+        <!-- 鍗冲皢瓒呮椂鎴鏃堕棿 = 鍑洪櫌鏃堕棿(endtime) + 鍙戦�佹棩(send_day, 缂虹渷1) + 琛ュ伩澶╂暟(compensate_time 姹傚拰鍚庡啀 +1 澶�)锛�
+             涓� RyTask/RedisMqReceiver 鐨� isSubtaskDeadline 鍙e緞淇濇寔涓�鑷达紱
+             鏃犳湁鏁堣ˉ鍋胯褰�(cp.subid 涓虹┖)鏃舵埅姝㈡椂闂翠负 NULL锛屼笉鍙備笌鍗冲皢瓒呮椂缁熻 -->
+        CASE
+        WHEN cp.subid IS NULL THEN NULL
+        ELSE DATE_ADD(
+        DATE_ADD(ss.endtime, INTERVAL COALESCE(st.send_day, 1) DAY),
+        INTERVAL (COALESCE(cp.compensate_days, 0) + 1) DAY
+        )
+        END AS soon_deadline
+        FROM service_subtask ss
+        LEFT JOIN service_task st ON st.taskid = ss.taskid AND st.del_flag = '0'
+        <!-- 琛ュ伩鏃堕棿锛氬厛鎸夊瓙浠诲姟鑱氬悎鎴愪竴琛屽啀鍏宠仈锛岄伩鍏嶄竴瀵瑰鏀惧ぇ璁℃暟锛�
+             compensate_time 鏄� varchar 涓斿彲鑳藉瓨鎴愮┖涓诧紝闇�鍏堢疆绌哄啀杞暟瀛� -->
+        LEFT JOIN (
+        SELECT subid,
+        SUM(CAST(COALESCE(NULLIF(TRIM(compensate_time), ''), '0') AS SIGNED)) AS compensate_days
+        FROM service_subtask_preachform
+        WHERE del_flag = 0
+        GROUP BY subid
+        ) cp ON cp.subid = ss.id
+        WHERE ss.del_flag = '0'
+        AND ss.service_type = '2'
+        AND ss.visit_count = 1
         <if test="orgid != null and orgid != ''">
-            AND orgid = #{orgid}
+            AND ss.orgid = #{orgid}
         </if>
         <if test="campusid != null and campusid != ''">
-            and campusid = #{campusid}
+            and ss.campusid = #{campusid}
         </if>
-        <if test="leavehospitaldistrictcodes != null and leavehospitaldistrictcodes.size() > 0">
-            AND leavehospitaldistrictcode IN
-            <foreach collection="leavehospitaldistrictcodes" item="leavehospitaldistrictcode" open="("
-                     separator=","
-                     close=")">
-                #{leavehospitaldistrictcode}
-            </foreach>
+        <!-- 绂婚櫌绉戝/鐥呭尯锛氭湁绉戝鎸夌瀹ゃ�佹湁鐥呭尯鎸夌梾鍖猴紝涓よ�呴兘鏈夋椂鍙� OR锛堝師鍐欐硶 1=1 浼氳绛涢�夊け鏁堬級 -->
+        <if test="(leavehospitaldistrictcodes != null and leavehospitaldistrictcodes.size() > 0)
+                or (leaveldeptcodes != null and leaveldeptcodes.size() > 0)">
+            AND (
+            <if test="leavehospitaldistrictcodes != null and leavehospitaldistrictcodes.size() > 0">
+                ss.leavehospitaldistrictcode IN
+                <foreach collection="leavehospitaldistrictcodes" item="leavehospitaldistrictcode" open="("
+                         separator=","
+                         close=")">
+                    #{leavehospitaldistrictcode}
+                </foreach>
+            </if>
+            <if test="(leavehospitaldistrictcodes != null and leavehospitaldistrictcodes.size() > 0)
+                and (leaveldeptcodes != null and leaveldeptcodes.size() > 0)">
+                OR
+            </if>
+            <if test=" leaveldeptcodes != null and leaveldeptcodes.size()>0">
+                ss.leaveldeptcode IN
+                <foreach collection="leaveldeptcodes" item="leaveldeptcode" open="(" separator=","
+                         close=")">
+                    #{leaveldeptcode}
+                </foreach>
+            </if>)
         </if>
-        <if test=" leaveldeptcodes != null and leaveldeptcodes.size()>0">
-            AND leaveldeptcode IN
-            <foreach collection="leaveldeptcodes" item="leaveldeptcode" open="(" separator=","
-                     close=")">
-                #{leaveldeptcode}
-            </foreach>
-        </if>
+        ) t
     </select>
 
             <!--
@@ -3677,7 +3912,8 @@
         continue_count,
         continue_time_next,
         task_situation,
-        remark
+        remark,
+        doctor_opinion
         from service_subtask
         where 1=1
         and del_flag = 0
diff --git a/ruoyi-task/src/main/resources/mapper/task/ServiceTaskMapper.xml b/ruoyi-task/src/main/resources/mapper/task/ServiceTaskMapper.xml
index ef4fb99..e2998b9 100644
--- a/ruoyi-task/src/main/resources/mapper/task/ServiceTaskMapper.xml
+++ b/ruoyi-task/src/main/resources/mapper/task/ServiceTaskMapper.xml
@@ -15,6 +15,7 @@
         <result property="unexecuted" column="unexecuted"/>
         <result property="fail" column="fail"/>
         <result property="sendDay" column="send_day"/>
+        <result property="delayFinishDays" column="delay_finish_days"/>
         <result property="checkuserid" column="checkuserid"/>
         <result property="checkusername" column="checkusername"/>
         <result property="checktime" column="checktime"/>
@@ -70,6 +71,7 @@
                pat_cycle,
                appltype,
                send_day,
+               delay_finish_days,
                long_task,
                show_date,
                compensate_date,
@@ -206,6 +208,7 @@
         service_task.task_name,
         service_task.appltype,
         service_task.send_day,
+        service_task.delay_finish_days,
         service_task.long_task,
         service_task.show_date,
         service_task.compensate_date,
@@ -488,6 +491,7 @@
             <if test="leavehospitaldistrictcode != null  ">leavehospitaldistrictcode,</if>
             <if test="compensateDate != null  and compensateDate != ''">compensate_date,</if>
             <if test="sendDay != null  ">send_day,</if>
+            <if test="delayFinishDays != null  ">delay_finish_days,</if>
             <if test="appltype != null  ">appltype,</if>
             <if test="patCycle != null  ">pat_cycle,</if>
             <if test="diagType != null and diagType != ''">diag_type,</if>
@@ -543,8 +547,9 @@
             <if test="jsy != null  and jsy != ''">#{jsy},</if>
             <if test="leavehospitaldistrictname != null  ">#{leavehospitaldistrictname},</if>
             <if test="leavehospitaldistrictcode != null  ">#{leavehospitaldistrictcode},</if>
-            <if test="compensateDate != null  and compensateDate != ''">#{compensateDate}</if>
+            <if test="compensateDate != null  and compensateDate != ''">#{compensateDate},</if>
             <if test="sendDay != null  ">#{sendDay},</if>
+            <if test="delayFinishDays != null  ">#{delayFinishDays},</if>
             <if test="appltype != null  ">#{appltype},</if>
             <if test="patCycle != null  ">#{patCycle},</if>
             <if test="diagType != null and diagType != ''">#{diagType},</if>
@@ -608,6 +613,7 @@
             </if>
             <if test="compensateDate != null  and compensateDate != ''">compensate_date = #{compensateDate},</if>
             <if test="sendDay != null  ">send_day = #{sendDay},</if>
+            <if test="delayFinishDays != null  ">delay_finish_days = #{delayFinishDays},</if>
             <if test="appltype != null  ">appltype = #{appltype},</if>
             <if test="patCycle != null  ">pat_cycle = #{patCycle},</if>
             <if test="diagType != null and diagType != ''">diag_type = #{diagType},</if>
diff --git a/ruoyi-task/src/main/resources/mapper/task/ServiceTaskdeptMapper.xml b/ruoyi-task/src/main/resources/mapper/task/ServiceTaskdeptMapper.xml
index 738003c..3314701 100644
--- a/ruoyi-task/src/main/resources/mapper/task/ServiceTaskdeptMapper.xml
+++ b/ruoyi-task/src/main/resources/mapper/task/ServiceTaskdeptMapper.xml
@@ -196,6 +196,9 @@
         where 1=1
             <if test="id != null">and id = #{id}</if>
             <if test="taskId != null">and task_id = #{taskId}</if>
+            <if test="deptType != null and deptType != ''">
+                AND dept_type = #{deptType}
+            </if>
             <if test="orgid != null and orgid != ''">
                 AND orgid = #{orgid}
             </if>
diff --git a/smartor/src/main/java/com/smartor/domain/DTO/ManualVisitDTO.java b/smartor/src/main/java/com/smartor/domain/DTO/ManualVisitDTO.java
new file mode 100644
index 0000000..223dfc8
--- /dev/null
+++ b/smartor/src/main/java/com/smartor/domain/DTO/ManualVisitDTO.java
@@ -0,0 +1,29 @@
+package com.smartor.domain.DTO;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 浜哄伐澶勭悊闅忚浠诲姟鍏ュ弬锛堝搴� /smartor/patinhosp/addManualVisit锛�
+ *
+ * <p>璇存槑锛歮aster 渚у凡璋冪敤璇ョ被鍨嬩絾浠撳簱涓紡鎻愪氦浜嗗疄浣撴枃浠讹紝
+ * 姝ゅ鎸� {@code PatMedInhospServiceImpl#addManualVisit} 鐨勫疄闄呯敤娉曡ˉ榻愶細
+ * 浠呬娇鐢� {@link #patids} 涓� {@link #taskid} 涓や釜瀛楁銆�</p>
+ *
+ * @author ruoyi
+ */
+@Data
+@ApiModel(value = "浜哄伐澶勭悊闅忚浠诲姟鍏ュ弬")
+public class ManualVisitDTO {
+
+    /** 鎮h�匢D闆嗗悎 */
+    @ApiModelProperty(value = "鎮h�匢D闆嗗悎")
+    private List<Long> patids;
+
+    /** 闅忚浠诲姟ID */
+    @ApiModelProperty(value = "闅忚浠诲姟ID")
+    private Long taskid;
+}
diff --git a/smartor/src/main/java/com/smartor/domain/LishuiFuyouWxChatRequest.java b/smartor/src/main/java/com/smartor/domain/LishuiFuyouWxChatRequest.java
new file mode 100644
index 0000000..36b00d2
--- /dev/null
+++ b/smartor/src/main/java/com/smartor/domain/LishuiFuyouWxChatRequest.java
@@ -0,0 +1,59 @@
+package com.smartor.domain;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 涓芥按濡囧辜寰俊娑堟伅璇锋眰锛堝搴� /sms/sendChatFuyou锛�
+ *
+ * <p>璇存槑锛歮aster 渚� {@code SmsController#sendChatFuyou} 閫氳繃 {@code com.smartor.domain.*}
+ * 閫氶厤瀵煎叆浣跨敤鏈被锛屼絾浠撳簱涓紡鎻愪氦浜嗗疄浣撴枃浠躲�傛澶勬寜 {@code SmsUtils.LsWxMessage}
+ * 鐨勫瓧娈典笌 {@code SmsController} 鐨勫疄闄呭彇鍊艰ˉ榻愩��</p>
+ *
+ * @author ruoyi
+ */
+@Data
+@ApiModel(value = "涓芥按濡囧辜寰俊娑堟伅璇锋眰")
+public class LishuiFuyouWxChatRequest implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /** 娑堟伅妯℃澘ID锛屼负绌烘椂浣跨敤閰嶇疆涓殑妯℃澘 */
+    @ApiModelProperty(value = "娑堟伅妯℃澘ID锛屼负绌烘椂浣跨敤閰嶇疆涓殑妯℃澘")
+    private Long templateId;
+
+    /** 鎮h�呯紪鍙� */
+    @ApiModelProperty(value = "鎮h�呯紪鍙�")
+    private String patientId;
+
+    /** 鎮h�呭鍚� */
+    @ApiModelProperty(value = "鎮h�呭鍚�")
+    private String patientName;
+
+    /** 璇佷欢鍙� */
+    @ApiModelProperty(value = "璇佷欢鍙�")
+    private String idCard;
+
+    /** 鎵嬫満鍙风爜 */
+    @ApiModelProperty(value = "鎵嬫満鍙风爜")
+    private String mobile;
+
+    /** 灏辫瘖鍗″彿 */
+    @ApiModelProperty(value = "灏辫瘖鍗″彿")
+    private String medCardNo;
+
+    /** 閫氱煡鏍囬 */
+    @ApiModelProperty(value = "閫氱煡鏍囬")
+    private String titleName;
+
+    /** 閫氱煡鍐呭 */
+    @ApiModelProperty(value = "閫氱煡鍐呭")
+    private String content;
+
+    /** 閾炬帴鍦板潃 */
+    @ApiModelProperty(value = "閾炬帴鍦板潃")
+    private String url;
+}
diff --git a/smartor/src/main/java/com/smartor/domain/PatChildArchive.java b/smartor/src/main/java/com/smartor/domain/PatChildArchive.java
index 7822b7b..013137d 100644
--- a/smartor/src/main/java/com/smartor/domain/PatChildArchive.java
+++ b/smartor/src/main/java/com/smartor/domain/PatChildArchive.java
@@ -1,5 +1,6 @@
 package com.smartor.domain;
 
+import java.math.BigDecimal;
 import java.util.Date;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
@@ -93,11 +94,6 @@
     @ApiModelProperty(value = "鎷熼殢璁挎椂闂�")
     private Date plannedFollowTime;
 
-    /** 闅忚鍛ㄦ湡(娆℃暟) */
-    @Excel(name = "闅忚鍛ㄦ湡(娆℃暟)")
-    @ApiModelProperty(value = "闅忚鍛ㄦ湡(娆℃暟)")
-    private String followupCycleTimes;
-
     /** 鎬у埆 1:鐢� 2:濂� */
     @Excel(name = "鎬у埆")
     @ApiModelProperty(value = "鎬у埆 1:鐢� 2:濂�")
@@ -153,11 +149,45 @@
     @ApiModelProperty(value = "璇婃柇鍚嶇О")
     private String diagname;
 
+    /** 棰勭害鎯呭喌 */
+    @ApiModelProperty(value = "棰勭害鎯呭喌")
+    private String appointmentSituation;
+
     /** 缁撴鐘舵�� */
     @Excel(name = "缁撴鐘舵��")
     @ApiModelProperty(value = "缁撴鐘舵��")
     private String closeStatus;
 
+    /** 钀ュ吇鎬х柧鐥匢D */
+    @Excel(name = "钀ュ吇鎬х柧鐥匢D")
+    @ApiModelProperty(value = "钀ュ吇鎬х柧鐥匢D")
+    private String nutritionIcd10Code;
+
+    /** 钀ュ吇鎬х柧鐥呭悕绉� */
+    @Excel(name = "钀ュ吇鎬х柧鐥呭悕绉�")
+    @ApiModelProperty(value = "钀ュ吇鎬х柧鐥呭悕绉�")
+    private String nutritionDiagName;
+
+    /** 鍑虹敓瀛曞懆 */
+    @Excel(name = "鍑虹敓瀛曞懆")
+    @ApiModelProperty(value = "鍑虹敓瀛曞懆")
+    private BigDecimal gestationalWeek;
+
+    /** 鍑虹敓浣撻噸(kg) */
+    @Excel(name = "鍑虹敓浣撻噸(kg)")
+    @ApiModelProperty(value = "鍑虹敓浣撻噸(kg)")
+    private BigDecimal birthWeight;
+
+    /** 楂樺嵄鍎垮垎绫� */
+    @Excel(name = "楂樺嵄鍎垮垎绫�")
+    @ApiModelProperty(value = "楂樺嵄鍎垮垎绫�")
+    private String highRiskChildType;
+
+    /** 楂樺嵄鍎垮悕绉� */
+    @Excel(name = "楂樺嵄鍎垮悕绉�")
+    @ApiModelProperty(value = "楂樺嵄鍎垮悕绉�")
+    private String highRiskChildName;
+
     /** 鐧昏浜哄憳 */
     @Excel(name = "鐧昏浜哄憳")
     @ApiModelProperty(value = "鐧昏浜哄憳")
diff --git a/smartor/src/main/java/com/smartor/domain/PatMaternityArchive.java b/smartor/src/main/java/com/smartor/domain/PatMaternityArchive.java
index be9ce5f..6d48ea4 100644
--- a/smartor/src/main/java/com/smartor/domain/PatMaternityArchive.java
+++ b/smartor/src/main/java/com/smartor/domain/PatMaternityArchive.java
@@ -60,7 +60,7 @@
     private String parentname;
 
     /** 鍑虹敓鏃ユ湡 */
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     @Excel(name = "鍑虹敓鏃ユ湡", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
     @ApiModelProperty(value = "鍑虹敓鏃ユ湡")
     private Date birthdate;
@@ -76,19 +76,19 @@
     private String phone;
 
     /** 璇婃柇鏃堕棿 */
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     @Excel(name = "璇婃柇鏃堕棿", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
     @ApiModelProperty(value = "璇婃柇鏃堕棿")
     private Date diagTime;
 
     /** 寤烘。鏃堕棿 */
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     @Excel(name = "寤烘。鏃堕棿", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
     @ApiModelProperty(value = "寤烘。鏃堕棿")
     private Date archiveTime;
 
     /** 鎷熼殢璁挎椂闂� */
-    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     @Excel(name = "鎷熼殢璁挎椂闂�", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
     @ApiModelProperty(value = "鎷熼殢璁挎椂闂�")
     private Date plannedFollowTime;
diff --git a/smartor/src/main/java/com/smartor/domain/PatMaternityArchiveDetail.java b/smartor/src/main/java/com/smartor/domain/PatMaternityArchiveDetail.java
index ce7e64b..af5f83b 100644
--- a/smartor/src/main/java/com/smartor/domain/PatMaternityArchiveDetail.java
+++ b/smartor/src/main/java/com/smartor/domain/PatMaternityArchiveDetail.java
@@ -190,6 +190,37 @@
     @ApiModelProperty(value = "闅忚鍛ㄦ湡")
     private String followupPeriod;
 
+    /** 闅忚鍛ㄦ湡(娆℃暟) */
+    @Excel(name = "闅忚鍛ㄦ湡(娆℃暟)")
+    @ApiModelProperty(value = "闅忚鍛ㄦ湡(娆℃暟)")
+    private String followupCycleTimes;
+
+    /** 鏈鏈堢粡 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @Excel(name = "鏈鏈堢粡", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
+    @ApiModelProperty(value = "鏈鏈堢粡")
+    private Date lastMenstrualTime;
+
+    /** 棣栬瘖鍖荤敓 */
+    @Excel(name = "棣栬瘖鍖荤敓")
+    @ApiModelProperty(value = "棣栬瘖鍖荤敓")
+    private String firstDoctorCode;
+
+    /** 缁忕鍖荤敓濮撳悕 */
+    @Excel(name = "缁忕鍖荤敓濮撳悕")
+    @ApiModelProperty(value = "缁忕鍖荤敓濮撳悕")
+    private String firstDoctorName;
+
+    /** 鑱旂郴浜鸿仈绯荤數璇� */
+    @Excel(name = "鑱旂郴浜鸿仈绯荤數璇�")
+    @ApiModelProperty(value = "鑱旂郴浜鸿仈绯荤數璇�")
+    private String contactPhone;
+
+    /** 灞呬綇鍦板潃 */
+    @Excel(name = "灞呬綇鍦板潃")
+    @ApiModelProperty(value = "灞呬綇鍦板潃")
+    private String address;
+
     /** 鏃╁瓡鍏崇埍-鏃╁瓡闅忚琛� */
     @ApiModelProperty(value = "鏃╁瓡鍏崇埍-鏃╁瓡闅忚琛�")
     private String epEarlyPregnancyFollowupForm;
diff --git a/smartor/src/main/java/com/smartor/domain/ServiceSubtaskExportVO.java b/smartor/src/main/java/com/smartor/domain/ServiceSubtaskExportVO.java
index 15eff92..d71db23 100644
--- a/smartor/src/main/java/com/smartor/domain/ServiceSubtaskExportVO.java
+++ b/smartor/src/main/java/com/smartor/domain/ServiceSubtaskExportVO.java
@@ -158,10 +158,10 @@
     private String templatename;
 
     /**
-     * 鍙戝鐞嗘剰瑙侊細1鏆備笉澶勭悊銆�2閫氱煡灏辫瘖銆�3鐥呮儏绋冲畾銆�4浜哄伐闅忚銆�5澶辫閫乁UID
+     * 鍙戝鐞嗘剰瑙侊細1鏆備笉澶勭悊銆�2閫氱煡灏辫瘖銆�3鐥呮儏绋冲畾銆�4浜哄伐闅忚銆�5澶辫閫乁UID銆�6鍥為櫌澶嶈瘖澶嶆煡
      */
-    @Excel(name = " 澶勭悊鎰忚锛�1鏆備笉澶勭悊銆�2閫氱煡灏辫瘖銆�3鐥呮儏绋冲畾銆�4浜哄伐闅忚銆�5澶辫 ")
-    @ApiModelProperty(value = "澶勭悊鎰忚锛�1鏆備笉澶勭悊銆�2閫氱煡灏辫瘖銆�3鐥呮儏绋冲畾銆�4浜哄伐闅忚銆�5澶辫")
+    @Excel(name = " 澶勭悊鎰忚锛�1鏆備笉澶勭悊銆�2閫氱煡灏辫瘖銆�3鐥呮儏绋冲畾銆�4浜哄伐闅忚銆�5澶辫銆�6鍥為櫌澶嶈瘖澶嶆煡 ")
+    @ApiModelProperty(value = "澶勭悊鎰忚锛�1鏆備笉澶勭悊銆�2閫氱煡灏辫瘖銆�3鐥呮儏绋冲畾銆�4浜哄伐闅忚銆�5澶辫銆�6鍥為櫌澶嶈瘖澶嶆煡")
     private String suggest;
 
     /**
diff --git a/smartor/src/main/java/com/smartor/mapper/PatChildArchiveDetailMapper.java b/smartor/src/main/java/com/smartor/mapper/PatChildArchiveDetailMapper.java
index dbf5b1f..96082b9 100644
--- a/smartor/src/main/java/com/smartor/mapper/PatChildArchiveDetailMapper.java
+++ b/smartor/src/main/java/com/smartor/mapper/PatChildArchiveDetailMapper.java
@@ -25,4 +25,6 @@
     public int deletePatChildArchiveDetailById(Long id);
 
     public int deletePatChildArchiveDetailByIds(Long[] ids);
+
+    public int deletePatChildArchiveDetailByArchiveIds(Long[] archiveIds);
 }
diff --git a/smartor/src/main/java/com/smartor/mapper/PatMaternityArchiveDetailMapper.java b/smartor/src/main/java/com/smartor/mapper/PatMaternityArchiveDetailMapper.java
index 8a7b74d..1e67ac3 100644
--- a/smartor/src/main/java/com/smartor/mapper/PatMaternityArchiveDetailMapper.java
+++ b/smartor/src/main/java/com/smartor/mapper/PatMaternityArchiveDetailMapper.java
@@ -25,4 +25,6 @@
     public int deletePatMaternityArchiveDetailById(Long id);
 
     public int deletePatMaternityArchiveDetailByIds(Long[] ids);
+
+    public int deletePatMaternityArchiveDetailByArchiveIds(Long[] archiveIds);
 }
diff --git a/smartor/src/main/java/com/smartor/service/IPatChildArchiveDetailService.java b/smartor/src/main/java/com/smartor/service/IPatChildArchiveDetailService.java
index 2d8f399..b46cf81 100644
--- a/smartor/src/main/java/com/smartor/service/IPatChildArchiveDetailService.java
+++ b/smartor/src/main/java/com/smartor/service/IPatChildArchiveDetailService.java
@@ -22,5 +22,7 @@
 
     public int deletePatChildArchiveDetailByIds(Long[] ids);
 
+    public int deletePatChildArchiveDetailByArchiveIds(Long[] archiveIds);
+
     public int deletePatChildArchiveDetailById(Long id);
 }
diff --git a/smartor/src/main/java/com/smartor/service/IPatChildArchiveService.java b/smartor/src/main/java/com/smartor/service/IPatChildArchiveService.java
index c32bf24..62b0f5c 100644
--- a/smartor/src/main/java/com/smartor/service/IPatChildArchiveService.java
+++ b/smartor/src/main/java/com/smartor/service/IPatChildArchiveService.java
@@ -1,6 +1,7 @@
 package com.smartor.service;
 
 import com.smartor.domain.PatChildArchive;
+import com.smartor.domain.PatChildArchiveDetail;
 
 import java.util.List;
 
@@ -22,5 +23,9 @@
 
     public int deletePatChildArchiveByIds(Long[] ids);
 
+    public int batchDeletePatChildArchiveByIds(Long[] ids);
+
     public int deletePatChildArchiveById(Long id);
+
+    public int insertOrUpdateChildArchive(PatChildArchiveDetail patChildArchiveDetail);
 }
diff --git a/smartor/src/main/java/com/smartor/service/IPatMaternityArchiveDetailService.java b/smartor/src/main/java/com/smartor/service/IPatMaternityArchiveDetailService.java
index e5e03b2..d46ee5d 100644
--- a/smartor/src/main/java/com/smartor/service/IPatMaternityArchiveDetailService.java
+++ b/smartor/src/main/java/com/smartor/service/IPatMaternityArchiveDetailService.java
@@ -22,5 +22,7 @@
 
     public int deletePatMaternityArchiveDetailByIds(Long[] ids);
 
+    public int deletePatMaternityArchiveDetailByArchiveIds(Long[] archiveIds);
+
     public int deletePatMaternityArchiveDetailById(Long id);
 }
diff --git a/smartor/src/main/java/com/smartor/service/IPatMaternityArchiveService.java b/smartor/src/main/java/com/smartor/service/IPatMaternityArchiveService.java
index 61c7145..d4dc017 100644
--- a/smartor/src/main/java/com/smartor/service/IPatMaternityArchiveService.java
+++ b/smartor/src/main/java/com/smartor/service/IPatMaternityArchiveService.java
@@ -1,6 +1,7 @@
 package com.smartor.service;
 
 import com.smartor.domain.PatMaternityArchive;
+import com.smartor.domain.PatMaternityArchiveDetail;
 
 import java.util.List;
 
@@ -22,5 +23,9 @@
 
     public int deletePatMaternityArchiveByIds(Long[] ids);
 
+    public int batchDeletePatMaternityArchiveByIds(Long[] ids);
+
     public int deletePatMaternityArchiveById(Long id);
+
+    public int insertOrUpdateMaternityArchive(PatMaternityArchiveDetail patMaternityArchiveDetail);
 }
diff --git a/smartor/src/main/java/com/smartor/service/IPatMedInhospService.java b/smartor/src/main/java/com/smartor/service/IPatMedInhospService.java
index 0dad6d2..0f37fa5 100644
--- a/smartor/src/main/java/com/smartor/service/IPatMedInhospService.java
+++ b/smartor/src/main/java/com/smartor/service/IPatMedInhospService.java
@@ -4,7 +4,9 @@
 import com.ruoyi.common.core.service.dto.PatMedRes;
 import com.smartor.domain.*;
 import com.ruoyi.inhospvisit.domain.PatMedInhospDTO;
+import com.smartor.domain.DTO.ManualVisitDTO;
 import java.util.List;
+import java.util.Map;
 import com.ruoyi.inhospvisit.domain.PatMedInhospVO;
 import com.ruoyi.inhospvisit.domain.PatMedInhosp;
 import com.ruoyi.common.core.domain.entity.PatArchive;
@@ -72,5 +74,30 @@
      */
     public List<PatMedInhosp> getDocAndPat(PatMedInhosp patMedInhosp);
 
+    /**
+     * 涓存椂鏂规硶锛屽鐞哾iagcheckFlag = 0鐨勬暟鎹紝閲嶆柊鐢熸垚闅忚浠诲姟
+     *
+     * @param
+     * @return 缁撴灉
+     */
+    public void dealIcd10Subtask(PatMedInhosp patMedInhospVo, String config);
+
+    /**
+     * 浜哄伐闅忚锛氬垹闄ゆ偅鑰呭嚭闄㈣褰曞搴旂殑鏈畬鎴愰殢璁夸换鍔★紝骞舵寜鎸囧畾浠诲姟閲嶆柊鐢熸垚
+     *
+     * @param manualVisitDTO 鎮h�卲atid闆嗗悎鍙婁汉宸ラ殢璁夸换鍔askid
+     * @return 澶勭悊缁撴灉
+     */
+    public Map<String, Object> addManualVisit(ManualVisitDTO manualVisitDTO);
+
     public ServiceSubtask boxedServiceSubtask(ServiceTask serviceTask, PatMedInhosp patMedInhosp1, PatArchive patArchive, String config, Integer type);
+
+    /**
+     * 鎸変换鍔¢厤缃殑鍙戦�佹柟寮忥紙preachform_desc锛夊垵濮嬪寲瀛愪换鍔″彂閫佹柟寮忔槑缁嗐��
+     *
+     * @param serviceSubtask 瀛愪换鍔�
+     * @param sendstate      鍙戦�佹柟寮忔槑缁嗗垵濮嬬姸鎬侊紝涓虹┖鏃堕粯璁� 1
+     * @return 澶勭悊鏉℃暟
+     */
+    public Integer addServiceSubtaskPreachform(ServiceSubtask serviceSubtask, String sendstate);
 }
diff --git a/smartor/src/main/java/com/smartor/service/impl/PatChildArchiveDetailServiceImpl.java b/smartor/src/main/java/com/smartor/service/impl/PatChildArchiveDetailServiceImpl.java
index f3eb204..12582df 100644
--- a/smartor/src/main/java/com/smartor/service/impl/PatChildArchiveDetailServiceImpl.java
+++ b/smartor/src/main/java/com/smartor/service/impl/PatChildArchiveDetailServiceImpl.java
@@ -54,6 +54,12 @@
     }
 
     @Override
+    public int deletePatChildArchiveDetailByArchiveIds(Long[] archiveIds)
+    {
+        return patChildArchiveDetailMapper.deletePatChildArchiveDetailByArchiveIds(archiveIds);
+    }
+
+    @Override
     public int deletePatChildArchiveDetailById(Long id)
     {
         return patChildArchiveDetailMapper.deletePatChildArchiveDetailById(id);
diff --git a/smartor/src/main/java/com/smartor/service/impl/PatChildArchiveServiceImpl.java b/smartor/src/main/java/com/smartor/service/impl/PatChildArchiveServiceImpl.java
index c7f4390..4dd9148 100644
--- a/smartor/src/main/java/com/smartor/service/impl/PatChildArchiveServiceImpl.java
+++ b/smartor/src/main/java/com/smartor/service/impl/PatChildArchiveServiceImpl.java
@@ -1,11 +1,19 @@
 package com.smartor.service.impl;
 
+import com.ruoyi.common.exception.base.BaseException;
 import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.common.utils.DtoConversionUtils;
+import com.ruoyi.common.utils.StringUtils;
 import com.smartor.domain.PatChildArchive;
+import com.smartor.domain.PatChildArchiveDetail;
 import com.smartor.mapper.PatChildArchiveMapper;
+import com.smartor.service.IPatChildArchiveDetailService;
 import com.smartor.service.IPatChildArchiveService;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
 import java.util.List;
 
@@ -15,11 +23,15 @@
  * @author ruoyi
  * @date 2026-10-09
  */
+@Slf4j
 @Service
 public class PatChildArchiveServiceImpl implements IPatChildArchiveService
 {
     @Autowired
     private PatChildArchiveMapper patChildArchiveMapper;
+
+    @Autowired
+    private IPatChildArchiveDetailService patChildArchiveDetailService;
 
     @Override
     public PatChildArchive selectPatChildArchiveById(Long id)
@@ -54,8 +66,118 @@
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
+    public int batchDeletePatChildArchiveByIds(Long[] ids)
+    {
+        // 鍏堝垹闄ゅ搴旂殑璇︽儏琛ㄦ暟鎹�
+        patChildArchiveDetailService.deletePatChildArchiveDetailByArchiveIds(ids);
+        // 鍐嶅垹闄や富琛ㄦ暟鎹�
+        return patChildArchiveMapper.deletePatChildArchiveByIds(ids);
+    }
+
+    @Override
     public int deletePatChildArchiveById(Long id)
     {
         return patChildArchiveMapper.deletePatChildArchiveById(id);
     }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public int insertOrUpdateChildArchive(PatChildArchiveDetail patChildArchiveDetail) {
+        if (patChildArchiveDetail == null) {
+            log.error("insertOrUpdateChildArchive鍏ュ弬涓虹┖锛岃妫�鏌ュ叆鍙�");
+            throw new BaseException("鍎跨妗f鍏ュ弬涓虹┖锛岃妫�鏌ュ叆鍙�");
+        }
+        if (StringUtils.isEmpty(patChildArchiveDetail.getOuthospno())) {
+            throw new BaseException("鍎跨妗f鍏ュ弬闂ㄨ瘖缂栧彿outhospno涓虹┖锛岃妫�鏌ュ叆鍙傞棬璇婄紪鍙�");
+        }
+        if (StringUtils.isEmpty(patChildArchiveDetail.getArchiveType())) {
+            throw new BaseException("鍎跨妗f鍏ュ弬妗f绫诲瀷archiveType涓虹┖锛岃妫�鏌ュ叆鍙傛。妗堢被鍨�");
+        }
+
+        // 鎸変笟鍔¢敭鏌ヨ鎯�
+        PatChildArchiveDetail key = new PatChildArchiveDetail();
+        key.setOrgid(patChildArchiveDetail.getOrgid());
+        key.setPatid(patChildArchiveDetail.getPatid());
+        key.setOuthospno(patChildArchiveDetail.getOuthospno());
+        key.setArchiveType(patChildArchiveDetail.getArchiveType());
+        List<PatChildArchiveDetail> olds =
+                patChildArchiveDetailService.selectPatChildArchiveDetailList(key);
+
+        Long existArchiveId = null;
+        Long existDetailId = null;
+        if (CollectionUtils.isNotEmpty(olds)) {
+            existArchiveId = olds.get(0).getArchiveid();
+            existDetailId = olds.get(0).getId();
+        }
+
+        // 璇︽儏娌℃煡鍒帮紝鍐嶅厹搴曟寜鍚屾牱閿煡涓昏〃锛岄伩鍏嶄富琛ㄩ噸澶�
+        if (existArchiveId == null) {
+            PatChildArchive mainKey = new PatChildArchive();
+            mainKey.setOrgid(patChildArchiveDetail.getOrgid());
+            mainKey.setPatid(patChildArchiveDetail.getPatid());
+            mainKey.setOuthospno(patChildArchiveDetail.getOuthospno());
+            mainKey.setArchiveType(patChildArchiveDetail.getArchiveType());
+            List<PatChildArchive> mainOlds =
+                    patChildArchiveMapper.selectPatChildArchiveList(mainKey);
+            if (CollectionUtils.isNotEmpty(mainOlds)) {
+                existArchiveId = mainOlds.get(0).getId();
+            }
+        }
+        patChildArchiveDetail.setArchiveid(existArchiveId);
+
+        // 鍚庨潰鏇存柊璇︽儏鏃讹紝浼樺厛鐢ㄤ笟鍔¢敭鍖归厤鍒扮殑 detail id
+        Long detailId = patChildArchiveDetail.getId();
+        if (detailId == null) {
+            detailId = existDetailId;
+        }
+
+
+        // 灏嗚鎯呬腑鐨勫叕鍏卞瓧娈靛鍒跺埌涓昏〃瀵硅薄
+        PatChildArchive patChildArchive = DtoConversionUtils.sourceToTarget(patChildArchiveDetail, PatChildArchive.class);
+        if (patChildArchive == null) {
+            throw new BaseException("鍎跨妗f瀵硅薄杞崲澶辫触锛岃妫�鏌ュ叆鍙�");
+        }
+        // 闃叉鎶婅鎯呰〃id锛坧atChildArchiveDetail.id锛夊綋浣滀富琛╥d锛屼富琛╥d蹇呴』浠rchiveid涓哄噯
+        patChildArchive.setId(patChildArchiveDetail.getArchiveid());
+
+        // 涓昏〃锛氭湁id涓斿簱涓瓨鍦ㄥ垯淇敼锛屽惁鍒欐柊澧�
+        if (patChildArchive.getId() != null
+                && patChildArchiveMapper.selectPatChildArchiveById(patChildArchive.getId()) != null) {
+            patChildArchive.setUpdateTime(DateUtils.getNowDate());
+            patChildArchiveMapper.updatePatChildArchive(patChildArchive);
+        } else {
+            patChildArchive.setCreateTime(DateUtils.getNowDate());
+            if (StringUtils.isEmpty(patChildArchive.getDelFlag())) {
+                patChildArchive.setDelFlag("0");
+            }
+            patChildArchiveMapper.insertPatChildArchive(patChildArchive);
+        }
+
+        // 鍥炲~涓昏〃id鍒拌鎯咃紝鍚屾鐢熸垚/鏇存柊璇︽儏琛�
+        patChildArchiveDetail.setArchiveid(patChildArchive.getId());
+
+//      Long detailId = patChildArchiveDetail.getId();
+        if (detailId == null && patChildArchiveDetail.getArchiveid() != null) {
+            PatChildArchiveDetail query = new PatChildArchiveDetail();
+            query.setArchiveid(patChildArchiveDetail.getArchiveid());
+            List<PatChildArchiveDetail> existList = patChildArchiveDetailService.selectPatChildArchiveDetailList(query);
+            if (CollectionUtils.isNotEmpty(existList)) {
+                detailId = existList.get(0).getId();
+            }
+        }
+
+        if (detailId != null) {
+            patChildArchiveDetail.setId(detailId);
+            patChildArchiveDetailService.updatePatChildArchiveDetail(patChildArchiveDetail);
+        } else {
+            if (StringUtils.isEmpty(patChildArchiveDetail.getDelFlag())) {
+                patChildArchiveDetail.setDelFlag("0");
+            }
+            patChildArchiveDetailService.insertPatChildArchiveDetail(patChildArchiveDetail);
+        }
+
+        log.info("insertOrUpdateChildArchive澶勭悊瀹屾垚锛屼富琛╥d锛歿}锛岃鎯卛d锛歿}", patChildArchive.getId(), patChildArchiveDetail.getId());
+        return patChildArchive.getId().intValue();
+    }
 }
diff --git a/smartor/src/main/java/com/smartor/service/impl/PatMaternityArchiveDetailServiceImpl.java b/smartor/src/main/java/com/smartor/service/impl/PatMaternityArchiveDetailServiceImpl.java
index 6f5c742..ba6bfb8 100644
--- a/smartor/src/main/java/com/smartor/service/impl/PatMaternityArchiveDetailServiceImpl.java
+++ b/smartor/src/main/java/com/smartor/service/impl/PatMaternityArchiveDetailServiceImpl.java
@@ -54,6 +54,12 @@
     }
 
     @Override
+    public int deletePatMaternityArchiveDetailByArchiveIds(Long[] archiveIds)
+    {
+        return patMaternityArchiveDetailMapper.deletePatMaternityArchiveDetailByArchiveIds(archiveIds);
+    }
+
+    @Override
     public int deletePatMaternityArchiveDetailById(Long id)
     {
         return patMaternityArchiveDetailMapper.deletePatMaternityArchiveDetailById(id);
diff --git a/smartor/src/main/java/com/smartor/service/impl/PatMaternityArchiveServiceImpl.java b/smartor/src/main/java/com/smartor/service/impl/PatMaternityArchiveServiceImpl.java
index 49b170a..cea1042 100644
--- a/smartor/src/main/java/com/smartor/service/impl/PatMaternityArchiveServiceImpl.java
+++ b/smartor/src/main/java/com/smartor/service/impl/PatMaternityArchiveServiceImpl.java
@@ -1,11 +1,22 @@
 package com.smartor.service.impl;
 
+import com.ruoyi.common.exception.base.BaseException;
 import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.common.utils.DtoConversionUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.smartor.domain.PatChildArchive;
+import com.smartor.domain.PatChildArchiveDetail;
 import com.smartor.domain.PatMaternityArchive;
+import com.smartor.domain.PatMaternityArchiveDetail;
+import com.smartor.mapper.PatMaternityArchiveDetailMapper;
 import com.smartor.mapper.PatMaternityArchiveMapper;
+import com.smartor.service.IPatMaternityArchiveDetailService;
 import com.smartor.service.IPatMaternityArchiveService;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
 import java.util.List;
 
@@ -15,11 +26,17 @@
  * @author ruoyi
  * @date 2026-10-09
  */
+@Slf4j
 @Service
 public class PatMaternityArchiveServiceImpl implements IPatMaternityArchiveService
 {
     @Autowired
     private PatMaternityArchiveMapper patMaternityArchiveMapper;
+
+    @Autowired
+    private IPatMaternityArchiveDetailService patMaternityArchiveDetailService;
+    @Autowired
+    private PatMaternityArchiveDetailMapper patMaternityArchiveDetailMapper;
 
     @Override
     public PatMaternityArchive selectPatMaternityArchiveById(Long id)
@@ -54,8 +71,136 @@
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
+    public int batchDeletePatMaternityArchiveByIds(Long[] ids)
+    {
+        // 鍏堝垹闄ゅ搴旂殑璇︽儏琛ㄦ暟鎹�
+        patMaternityArchiveDetailService.deletePatMaternityArchiveDetailByArchiveIds(ids);
+        // 鍐嶅垹闄や富琛ㄦ暟鎹�
+        return patMaternityArchiveMapper.deletePatMaternityArchiveByIds(ids);
+    }
+
+    @Override
     public int deletePatMaternityArchiveById(Long id)
     {
         return patMaternityArchiveMapper.deletePatMaternityArchiveById(id);
     }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public int insertOrUpdateMaternityArchive(PatMaternityArchiveDetail patMaternityArchiveDetail) {
+        if (patMaternityArchiveDetail == null) {
+            log.error("insertOrUpdateMaternityArchive鍏ュ弬涓虹┖锛岃妫�鏌ュ叆鍙�");
+            throw new BaseException("濡囩妗f鍏ュ弬涓虹┖锛岃妫�鏌ュ叆鍙�");
+        }
+
+        //淇濊瘉鍚屼竴灏辫瘖璁板綍鍚屼竴妗f绫诲瀷鍙兘鐢熸垚涓�鏉�
+        PatMaternityArchiveDetail patMaternityArchiveDetailVo = new PatMaternityArchiveDetail();
+        patMaternityArchiveDetailVo.setOrgid(patMaternityArchiveDetail.getOrgid());
+        patMaternityArchiveDetailVo.setPatid(patMaternityArchiveDetail.getPatid());
+        patMaternityArchiveDetailVo.setOuthospno(patMaternityArchiveDetail.getOuthospno());
+        patMaternityArchiveDetailVo.setArchiveType(patMaternityArchiveDetail.getArchiveType());
+        List<PatMaternityArchiveDetail> patMaternityArchiveDetailOlds
+                = patMaternityArchiveDetailService.selectPatMaternityArchiveDetailList(patMaternityArchiveDetailVo);
+
+
+        if (StringUtils.isEmpty(patMaternityArchiveDetail.getOuthospno())) {
+            throw new BaseException("濡囩妗f鍏ュ弬闂ㄨ瘖缂栧彿outhospno涓虹┖锛岃妫�鏌ュ叆鍙傞棬璇婄紪鍙�");
+        }
+        if (StringUtils.isEmpty(patMaternityArchiveDetail.getArchiveType())) {
+            throw new BaseException("濡囩妗f鍏ュ弬妗f绫诲瀷archiveType涓虹┖锛岃妫�鏌ュ叆鍙傛。妗堢被鍨�");
+        }
+
+        // 鎸変笟鍔¢敭鏌ヨ鎯�
+        PatMaternityArchiveDetail key = new PatMaternityArchiveDetail();
+        key.setOrgid(patMaternityArchiveDetail.getOrgid());
+        key.setPatid(patMaternityArchiveDetail.getPatid());
+        key.setOuthospno(patMaternityArchiveDetail.getOuthospno());
+        key.setArchiveType(patMaternityArchiveDetail.getArchiveType());
+        List<PatMaternityArchiveDetail> olds =
+                patMaternityArchiveDetailService.selectPatMaternityArchiveDetailList(key);
+
+        Long existArchiveId = null;
+        Long existDetailId = null;
+        if (CollectionUtils.isNotEmpty(olds)) {
+            existArchiveId = olds.get(0).getArchiveid();
+            existDetailId = olds.get(0).getId();
+        }
+
+        // 璇︽儏娌℃煡鍒帮紝鍐嶅厹搴曟寜鍚屾牱閿煡涓昏〃锛岄伩鍏嶄富琛ㄩ噸澶�
+        if (existArchiveId == null) {
+            PatMaternityArchive mainKey = new PatMaternityArchive();
+            mainKey.setOrgid(patMaternityArchiveDetail.getOrgid());
+            mainKey.setPatid(patMaternityArchiveDetail.getPatid());
+            mainKey.setOuthospno(patMaternityArchiveDetail.getOuthospno());
+            mainKey.setArchiveType(patMaternityArchiveDetail.getArchiveType());
+            List<PatMaternityArchive> mainOlds =
+                    patMaternityArchiveMapper.selectPatMaternityArchiveList(mainKey);
+            if (CollectionUtils.isNotEmpty(mainOlds)) {
+                existArchiveId = mainOlds.get(0).getId();
+            }
+        }
+        patMaternityArchiveDetail.setArchiveid(existArchiveId);
+
+        // 鍚庨潰鏇存柊璇︽儏鏃讹紝浼樺厛鐢ㄤ笟鍔¢敭鍖归厤鍒扮殑 detail id
+        Long detailId = patMaternityArchiveDetail.getId();
+        if (detailId == null) {
+            detailId = existDetailId;
+        }
+
+        // 鍙紶浜嗚鎯卛d鏃讹紝鍙嶆煡鍑哄搴旂殑妗f涓婚敭id锛屼繚璇佸悗缁兘姝g‘鏇存柊涓昏〃
+        if (CollectionUtils.isNotEmpty(patMaternityArchiveDetailOlds)) {
+            PatMaternityArchiveDetail existDetail = patMaternityArchiveDetailOlds.get(0);
+            if (existDetail != null) {
+                patMaternityArchiveDetail.setArchiveid(existDetail.getArchiveid());
+            }
+        }
+
+        // 灏嗚鎯呬腑鐨勫叕鍏卞瓧娈靛鍒跺埌涓昏〃瀵硅薄
+        PatMaternityArchive patMaternityArchive = DtoConversionUtils.sourceToTarget(patMaternityArchiveDetail, PatMaternityArchive.class);
+        if (patMaternityArchive == null) {
+            throw new BaseException("濡囩妗f瀵硅薄杞崲澶辫触锛岃妫�鏌ュ叆鍙�");
+        }
+        // 闃叉鎶婅鎯呰〃id锛坧atMaternityArchiveDetail.id锛夊綋浣滀富琛╥d锛屼富琛╥d蹇呴』浠rchiveid涓哄噯
+        patMaternityArchive.setId(patMaternityArchiveDetail.getArchiveid());
+
+        // 涓昏〃锛氭湁id涓斿簱涓瓨鍦ㄥ垯淇敼锛屽惁鍒欐柊澧�
+        if (patMaternityArchive.getId() != null
+                && patMaternityArchiveMapper.selectPatMaternityArchiveById(patMaternityArchive.getId()) != null) {
+            patMaternityArchive.setUpdateTime(DateUtils.getNowDate());
+            patMaternityArchiveMapper.updatePatMaternityArchive(patMaternityArchive);
+        } else {
+            patMaternityArchive.setCreateTime(DateUtils.getNowDate());
+            if (StringUtils.isEmpty(patMaternityArchive.getDelFlag())) {
+                patMaternityArchive.setDelFlag("0");
+            }
+            patMaternityArchiveMapper.insertPatMaternityArchive(patMaternityArchive);
+        }
+
+        // 鍥炲~涓昏〃id鍒拌鎯咃紝鍚屾鐢熸垚/鏇存柊璇︽儏琛�
+        patMaternityArchiveDetail.setArchiveid(patMaternityArchive.getId());
+
+//        Long detailId = patMaternityArchiveDetail.getId();
+        if (detailId == null && patMaternityArchiveDetail.getArchiveid() != null) {
+            PatMaternityArchiveDetail query = new PatMaternityArchiveDetail();
+            query.setArchiveid(patMaternityArchiveDetail.getArchiveid());
+            List<PatMaternityArchiveDetail> existList = patMaternityArchiveDetailService.selectPatMaternityArchiveDetailList(query);
+            if (CollectionUtils.isNotEmpty(existList)) {
+                detailId = existList.get(0).getId();
+            }
+        }
+
+        if (detailId != null) {
+            patMaternityArchiveDetail.setId(detailId);
+            patMaternityArchiveDetailService.updatePatMaternityArchiveDetail(patMaternityArchiveDetail);
+        } else {
+            if (StringUtils.isEmpty(patMaternityArchiveDetail.getDelFlag())) {
+                patMaternityArchiveDetail.setDelFlag("0");
+            }
+            patMaternityArchiveDetailService.insertPatMaternityArchiveDetail(patMaternityArchiveDetail);
+        }
+
+        log.info("insertOrUpdateMaternityArchive澶勭悊瀹屾垚锛屼富琛╥d锛歿}锛岃鎯卛d锛歿}", patMaternityArchive.getId(), patMaternityArchiveDetail.getId());
+        return patMaternityArchive.getId().intValue();
+    }
 }
diff --git a/smartor/src/main/java/com/smartor/service/impl/PatMedInhospServiceImpl.java b/smartor/src/main/java/com/smartor/service/impl/PatMedInhospServiceImpl.java
index 6c08431..cb6bec8 100644
--- a/smartor/src/main/java/com/smartor/service/impl/PatMedInhospServiceImpl.java
+++ b/smartor/src/main/java/com/smartor/service/impl/PatMedInhospServiceImpl.java
@@ -7,11 +7,14 @@
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.ruoyi.common.core.service.dto.PatMedReq;
 import com.ruoyi.common.core.service.dto.PatMedRes;
+import com.ruoyi.common.core.service.IConfigService;
+import com.ruoyi.common.exception.base.BaseException;
 import com.ruoyi.common.utils.DateUtils;
 import com.ruoyi.common.utils.DtoConversionUtils;
 import com.ruoyi.common.utils.StringUtils;
 import com.smartor.domain.*;
 import com.ruoyi.inhospvisit.domain.PatMedInhospDTO;
+import com.smartor.domain.DTO.ManualVisitDTO;
 import com.smartor.mapper.*;
 import com.smartor.service.IPatMedInhospService;
 import lombok.extern.slf4j.Slf4j;
@@ -19,11 +22,14 @@
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Isolation;
+import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.ObjectUtils;
 
 import java.time.Instant;
 import java.time.temporal.ChronoUnit;
 import java.util.*;
+import java.util.stream.Collectors;
 import com.ruoyi.othervisit.domain.PatMedOperationItem;
 import com.ruoyi.inhospvisit.domain.PatMedInhospVO;
 import com.ruoyi.inhospvisit.domain.PatMedInhosp;
@@ -87,6 +93,11 @@
     private ServiceSubtaskPreachformMapper serviceSubtaskPreachformMapper;
     @Autowired
     private ServiceTaskRuleMapper serviceTaskRuleMapper;
+    /**
+     * 鍙傛暟閰嶇疆锛堝疄鐜板湪 ruoyi-system锛夛紝浜哄伐闅忚 addManualVisit 闇�瑕佸湪鏈嶅姟灞傝鍙� visit.early.day銆�
+     */
+    @Autowired
+    private IConfigService iSysConfigService;
 
     @Value("${spring.profiles.active}")
     private String active;
@@ -412,6 +423,29 @@
         //璁板綍insertServiceSubtask璇彞杩斿洖鐘舵��
         Integer i = 0;
         try {
+            // 鍏滃簳锛氫换鍔℃ā鏉夸慨鏀逛細閲嶇疆 pat_med_inhosp 鐨勫鐞嗘爣璁帮紝浣嗗凡瀹屾垚鐨勯璁夸笉浼氬垹闄ゃ��
+            // 鍚屼竴 inhospid + taskid 宸插瓨鍦ㄦ湭鍒犻櫎鐨勯娆¢殢璁匡紙upid 涓虹┖锛夋椂锛屼笉鍐嶆彃鍏ワ紝閬垮厤鍑虹幇涓ゆ潯棣栨闅忚銆�
+            if (check
+                    && ("2".equals(serviceTask.getServiceType()) || "13".equals(serviceTask.getServiceType()))
+                    && (type == 1 || type == 2 || type == 3)) {
+                ServiceSubtaskEntity firstQuery = new ServiceSubtaskEntity();
+                firstQuery.setInhospid(patMedInhosp1.getInhospid());
+                firstQuery.setTaskid(taskid);
+                List<ServiceSubtask> firstList = serviceSubtaskMapper.selectServiceSubtaskList(firstQuery);
+                boolean firstExists = CollectionUtils.isNotEmpty(firstList)
+                        && firstList.stream().anyMatch(item -> item.getUpid() == null);
+                if (firstExists) {
+                    log.info("鎮h�厈}宸插瓨鍦ㄩ娆¢殢璁夸换鍔★紝璺宠繃閲嶅鐢熸垚, taskid={}, type={}",
+                            patMedInhosp1.getInhospid(), taskid, type);
+                    PatMedInhosp flagUpdate = new PatMedInhosp();
+                    flagUpdate.setInhospid(patMedInhosp1.getInhospid());
+                    if (type == 1) flagUpdate.setDeptcheckFlag("1");
+                    if (type == 2) flagUpdate.setWardcheckFlag("1");
+                    if (type == 3) flagUpdate.setDiagcheckFlag("1");
+                    inhospVisitFacade.updatePatMedInhosp(flagUpdate);
+                    return;
+                }
+            }
             //鍏堝垽鏂竴涓嬶紝鏄惁闇�瑕佹牎楠�
             if (check && (Objects.isNull(serviceSubtask.getTaskSituation()) || serviceSubtask.getTaskSituation() != 6)) {
                 //鍦ㄦ柊澧炰箣鍓嶏紝鍏堥�氳繃鎮h�匢D锛宻endstate=2鏌ヨ涓�涓嬶紝鍦ㄦ墍鏈夐暱鏈熶换鍔′腑锛屾槸涓嶆槸杩樻湁璇ユ偅鑰呭緟鎵ц鐨勪换鍔★紝鏈夌殑璇�,姣旇緝涔嬪墠鐨別ndtime鏄惁灏忎簬褰撳墠鐨別ndtaime,濡傛灉涔嬪墠鐨勫皬浜庣幇鍦ㄧ殑锛屽垯鐩存帴灏嗕箣鍓嶇殑鍋滄帀锛堝師鍥犲啀鍏ラ櫌锛�
@@ -716,7 +750,8 @@
         }
     }
 
-    private Integer addServiceSubtaskPreachform(ServiceSubtask serviceSubtask, String sendstate) {
+    @Override
+    public Integer addServiceSubtaskPreachform(ServiceSubtask serviceSubtask, String sendstate) {
         //灏唒reachformDesc鐨刯son瀵硅薄锛屾斁鍒發ist闆嗗悎涓�
         ServiceTask serviceTask = serviceTaskMapper.selectServiceTaskByTaskid(serviceSubtask.getTaskid());
         if (Objects.isNull(serviceTask)) {
@@ -1367,4 +1402,187 @@
         return generatedCount;
     }
 
+    @Override
+    public void dealIcd10Subtask(PatMedInhosp pmks, String config) {
+        pmks.setDiagcheckFlag("0");
+        pmks.setInhospstate("1");
+        pmks.setFuflag("1");
+        List<PatMedInhosp> patMedInhosps = inhospVisitFacade.selectPatMedInhospList(pmks);
+        log.info("dealIcd10Subtask 寰呭鐞嗗嚭闄㈢柧鐥呮偅鑰呮暟锛歿}", CollectionUtils.isEmpty(patMedInhosps) ? 0 : patMedInhosps.size());
+        if (CollectionUtils.isNotEmpty(patMedInhosps)) {
+            int matchedTaskCount = 0;
+            for (PatMedInhosp patMedInhosp : patMedInhosps) {
+                // 鑾峰彇鎮h�呭熀鏈俊鎭紝妗f涓嶅瓨鍦ㄦ椂鐩存帴鏍囪澶辫触锛岄伩鍏嶅悗缁┖鎸囬拡
+                PatArchive patArchive = patArchiveMapper.selectPatArchiveByPatid(patMedInhosp.getPatid());
+                if (Objects.isNull(patArchive)) {
+                    PatMedInhosp updateObj = new PatMedInhosp();
+                    updateObj.setInhospid(patMedInhosp.getInhospid());
+                    updateObj.setDiagcheckFlag("2");
+                    updateObj.setLongTaskReason("鎮h�呭熀鏈俊鎭负绌�");
+                    inhospVisitFacade.updatePatMedInhosp(updateObj);
+                    continue;
+                }
+                matchedTaskCount += addIcd10SubTaskForPatient(patMedInhosp, patArchive, config);
+            }
+            log.info("dealIcd10Subtask 澶勭悊瀹屾垚锛屽緟澶勭悊锛歿}锛屽尮閰嶇柧鐥呴暱鏈熶换鍔℃鏁帮細{}", patMedInhosps.size(), matchedTaskCount);
+        }
+    }
+
+    /**
+     * 浜哄伐闅忚锛氬垹闄ゆ偅鑰呭嚭闄㈣褰曞搴旂殑鏈畬鎴愰殢璁夸换鍔★紝骞舵寜鎸囧畾浠诲姟閲嶆柊鐢熸垚銆�
+     * 鍒犻櫎鏃т换鍔″彛寰勶細sendstate in (1,2,3,7)锛�
+     * 閲嶅浠诲姟鏍¢獙鍙e緞锛歴endstate in (1,2,3,5,7)锛屽凡鏈夌浉鍚� taskid 鏈畬鎴愪换鍔$殑鎮h�呰烦杩囥��
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
+    public Map<String, Object> addManualVisit(ManualVisitDTO manualVisitDTO) {
+        if (manualVisitDTO == null) {
+            throw new BaseException("鍏ュ弬涓嶈兘涓虹┖");
+        }
+        if (CollectionUtils.isEmpty(manualVisitDTO.getPatids())) {
+            throw new BaseException("鎮h�卲atid闆嗗悎涓嶈兘涓虹┖");
+        }
+        if (manualVisitDTO.getTaskid() == null) {
+            throw new BaseException("浜哄伐闅忚浠诲姟taskid涓嶈兘涓虹┖");
+        }
+
+        ServiceTask serviceTask = serviceTaskMapper.selectServiceTaskByTaskid(manualVisitDTO.getTaskid());
+        if (Objects.isNull(serviceTask)) {
+            throw new BaseException("浜哄伐闅忚浠诲姟涓嶅瓨鍦紝taskid锛�" + manualVisitDTO.getTaskid());
+        }
+
+        // 鍗囧簭澶勭悊锛岄厤鍚堟偅鑰呮。妗堣閿侊紝閬垮厤鎵归噺璇锋眰浜ゅ弶鍔犻攣浜х敓姝婚攣
+        List<Long> patids = manualVisitDTO.getPatids().stream()
+                .filter(Objects::nonNull)
+                .distinct()
+                .sorted()
+                .collect(Collectors.toList());
+        if (patids.isEmpty()) {
+            throw new BaseException("鎮h�卲atid闆嗗悎涓嶈兘涓虹┖");
+        }
+
+        String config = iSysConfigService.selectConfigByKey("visit.early.day");
+        List<Long> unfinishedSendstates = Arrays.asList(1L, 2L, 3L, 7L);
+        // 涓庡墠绔緟闅忚鍙e緞淇濇寔涓�鑷达紝鍙戦�佸け璐�(5)涔熻涓哄凡鏈夋湭瀹屾垚浠诲姟
+        List<Long> activeSendstates = Arrays.asList(1L, 2L, 3L, 5L, 7L);
+        int deletedCount = 0;
+        int generatedCount = 0;
+        List<Long> noDischargeRecordPatids = new ArrayList<>();
+        List<Long> noArchivePatids = new ArrayList<>();
+        List<Long> generateFailedPatids = new ArrayList<>();
+        List<Long> duplicatePatids = new ArrayList<>();
+        List<String> duplicatePatientNames = new ArrayList<>();
+
+        for (Long patid : patids) {
+            // 鏌ヨ鎮h�呭叏閮ㄥ嚭闄㈣褰曪紝SQL 鎸夊嚭闄㈡椂闂村�掑簭锛岀涓�鏉′綔涓烘湰娆$敓鎴愪换鍔$殑鍑洪櫌璁板綍
+            PatMedInhosp query = new PatMedInhosp();
+            query.setPatid(patid);
+            query.setCry(1);
+            if (StringUtils.isNotEmpty(serviceTask.getOrgid())) {
+                query.setOrgid(serviceTask.getOrgid());
+            }
+            List<PatMedInhosp> inhospList = inhospVisitFacade.selectPatMedInhospList(query);
+            if (CollectionUtils.isEmpty(inhospList)) {
+                noDischargeRecordPatids.add(patid);
+                continue;
+            }
+
+            PatMedInhosp latestInhosp = inhospList.get(0);
+            if (Objects.isNull(latestInhosp.getEndtime())) {
+                noDischargeRecordPatids.add(patid);
+                continue;
+            }
+
+            PatArchive patArchive = patArchiveMapper.selectPatArchiveByPatid(patid);
+            if (Objects.isNull(patArchive)) {
+                noArchivePatids.add(patid);
+                continue;
+            }
+
+            // 閿佸畾鎮h�呮。妗堣锛屼覆琛屽寲鍚屼竴鎮h�呯殑骞跺彂鍒涘缓璇锋眰
+            Long lockedPatid = patArchiveMapper.lockPatArchiveByPatid(patid);
+            if (Objects.isNull(lockedPatid)) {
+                noArchivePatids.add(patid);
+                continue;
+            }
+
+            // 鍚屼竴鎮h�呭湪鍚屼竴 taskid 涓嬪凡鏈夋湭瀹屾垚闅忚浠诲姟鏃讹紝璺宠繃璇ユ偅鑰咃紝閬垮厤閲嶅鍒涘缓
+            int existingSubtaskCount = serviceSubtaskMapper.countUnfinishedServiceSubtaskByPatidAndTaskid(
+                    patid, serviceTask.getTaskid(), activeSendstates);
+            if (existingSubtaskCount > 0) {
+                duplicatePatids.add(patid);
+                String patientName = StringUtils.isNotEmpty(patArchive.getName())
+                        ? patArchive.getName() : String.valueOf(patid);
+                duplicatePatientNames.add(patientName + "(" + patid + ")");
+                log.info("addManualVisit 鎮h�呭凡鏈夌浉鍚屾湇鍔★紝璺宠繃鐢熸垚锛宲atid锛歿}锛宼askid锛歿}锛屽凡鏈変换鍔℃暟锛歿}",
+                        patid, serviceTask.getTaskid(), existingSubtaskCount);
+                continue;
+            }
+
+            // 鍏堟瀯寤烘柊浠诲姟锛屾瀯寤哄け璐ユ椂涓嶅垹闄ゅ師浠诲姟锛岄伩鍏嶆偅鑰呬涪澶卞緟闅忚浠诲姟
+            ServiceSubtask serviceSubtask = boxedServiceSubtask(serviceTask, latestInhosp, patArchive, config, 1);
+            if (Objects.isNull(serviceSubtask) || Objects.isNull(serviceSubtask.getTaskid())) {
+                generateFailedPatids.add(patid);
+                continue;
+            }
+
+            List<Long> inhospids = inhospList.stream()
+                    .map(PatMedInhosp::getInhospid)
+                    .filter(Objects::nonNull)
+                    .distinct()
+                    .collect(Collectors.toList());
+            if (CollectionUtils.isNotEmpty(inhospids)) {
+                deletedCount += serviceSubtaskMapper.deleteUnfinishedServiceSubtaskByInhospids(
+                        patid, inhospids, unfinishedSendstates);
+            }
+
+            serviceSubtask.setId(null);
+            serviceSubtask.setUpid(null);
+            serviceSubtask.setVisitDeptCode(StringUtils.isNotEmpty(latestInhosp.getLeaveldeptcode())
+                    ? latestInhosp.getLeaveldeptcode() : latestInhosp.getLeavehospitaldistrictcode());
+            serviceSubtask.setVisitDeptName(StringUtils.isNotEmpty(latestInhosp.getLeaveldeptname())
+                    ? latestInhosp.getLeaveldeptname() : latestInhosp.getLeavehospitaldistrictname());
+            // 涓庡父瑙勭敓鎴愰�昏緫淇濇寔涓�鑷达細鎵嬫満鍙蜂负绌烘椂鐢熸垚浠诲姟浣嗕笉鎵ц
+            if (StringUtils.isEmpty(serviceSubtask.getPhone())) {
+                serviceSubtask.setRemark(StringUtils.isNotEmpty(serviceSubtask.getRemark())
+                        ? serviceSubtask.getRemark() + ",鎵嬫満鍙蜂负绌�" : "鎵嬫満鍙蜂负绌�");
+                serviceSubtask.setSendstate(4L);
+                serviceSubtask.setResult("error");
+            }
+
+            int inserted = serviceSubtaskMapper.insertServiceSubtask(serviceSubtask);
+            if (inserted != 1) {
+                throw new BaseException("鐢熸垚闅忚浠诲姟澶辫触锛宲atid锛�" + patid + "锛宼askid锛�" + serviceTask.getTaskid());
+            }
+            addServiceSubtaskPreachform(serviceSubtask, null);
+            generatedCount++;
+            log.info("addManualVisit 鐢熸垚闅忚浠诲姟鎴愬姛锛宲atid锛歿}锛宼askid锛歿}锛宻ubtaskId锛歿}",
+                    patid, serviceTask.getTaskid(), serviceSubtask.getId());
+        }
+
+        String tipMessage = null;
+        if (CollectionUtils.isNotEmpty(duplicatePatids)) {
+            String taskName = StringUtils.isNotEmpty(serviceTask.getTaskName())
+                    ? serviceTask.getTaskName() : String.valueOf(serviceTask.getTaskid());
+            tipMessage = "鎮h��" + String.join("銆�", duplicatePatientNames)
+                    + "宸叉湁鐩稿悓鏈嶅姟銆�" + taskName + "銆戯紝宸茶烦杩�";
+            if (generatedCount > 0) {
+                tipMessage += "锛涘叾浣欐偅鑰呭凡澶勭悊";
+            }
+        }
+
+        Map<String, Object> result = new HashMap<>();
+        result.put("deletedCount", deletedCount);
+        result.put("generatedCount", generatedCount);
+        result.put("noDischargeRecordPatids", noDischargeRecordPatids);
+        result.put("noArchivePatids", noArchivePatids);
+        result.put("generateFailedPatids", generateFailedPatids);
+        result.put("duplicatePatids", duplicatePatids);
+        result.put("duplicatePatientNames", duplicatePatientNames);
+        result.put("hasDuplicate", CollectionUtils.isNotEmpty(duplicatePatids));
+        result.put("tipMessage", tipMessage);
+        log.info("addManualVisit 澶勭悊瀹屾垚锛宲atid鏁帮細{}锛宼askid锛歿}锛屽垹闄ゆ湭瀹屾垚浠诲姟鏁帮細{}锛岀敓鎴愪换鍔℃暟锛歿}锛岄噸澶嶈烦杩囨暟锛歿}",
+                patids.size(), serviceTask.getTaskid(), deletedCount, generatedCount, duplicatePatids.size());
+        return result;
+    }
 }
diff --git a/smartor/src/main/java/com/smartor/service/impl/ServiceExternalServiceImpl.java b/smartor/src/main/java/com/smartor/service/impl/ServiceExternalServiceImpl.java
index b77772c..7c52ef0 100644
--- a/smartor/src/main/java/com/smartor/service/impl/ServiceExternalServiceImpl.java
+++ b/smartor/src/main/java/com/smartor/service/impl/ServiceExternalServiceImpl.java
@@ -3,6 +3,9 @@
 import com.ruoyi.othervisit.api.OtherVisitFacade;
 import com.ruoyi.inhospvisit.api.InhospVisitFacade;
 import cn.hutool.core.bean.BeanUtil;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
 import com.google.gson.Gson;
 import com.google.gson.reflect.TypeToken;
 import com.ruoyi.common.core.domain.entity.SysDept;
@@ -13,8 +16,11 @@
 import com.ruoyi.common.utils.DtoConversionUtils;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.http.HttpUtils;
+import com.ruoyi.system.service.ISysConfigService;
 import com.smartor.domain.*;
 import com.smartor.mapper.*;
+import com.smartor.service.IPatChildArchiveService;
+import com.smartor.service.IPatMaternityArchiveService;
 import com.smartor.service.IPatMedOuthospService;
 import com.smartor.service.IServiceExternalService;
 import lombok.extern.slf4j.Slf4j;
@@ -91,6 +97,12 @@
     private Icd10Mapper icd10Mapper;
     @Autowired
     private IPatMedOuthospService patMedOuthospService;
+    @Autowired
+    private ISysConfigService configService;
+    @Autowired
+    private IPatMaternityArchiveService patMaternityArchiveService;
+    @Autowired
+    private IPatChildArchiveService patChildArchiveService;
     @Autowired
     private PatMedOuthospMapper patMedOuthospMapper;
     @Autowired
@@ -1333,6 +1345,8 @@
             patMedOuthospService.dealPatMedOuthosp(patMedOuthosp, visitTimeNum);
         }
 
+        genePatSpecificArchive(patMedOuthosp, patArchive);
+
         if (i > 0) {
             return true;
         }
@@ -2006,75 +2020,192 @@
     }
 
     //妫�鏌ヨ鎯呮坊鍔�
+    @SuppressWarnings("unchecked")
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public Boolean addInspectBGService(Map dataMap) {
         log.info("ServiceExternalServiceImpl---addInspectBGService鐨勬柊澧炵殑鍊间负锛歿}", dataMap);
         Map xiaoXiTou = (Map) dataMap.get("XiaoXiTou");
         Map yeWuXX = (Map) dataMap.get("YeWuXX");
         Map<String, Object> BingRenXX = ObjectUtils.isNotEmpty(yeWuXX) ? (Map<String, Object>) yeWuXX.get("BingRenXX") : null;
         Map<String, Object> JianChaBG = ObjectUtils.isNotEmpty(yeWuXX) ? (Map<String, Object>) yeWuXX.get("JianChaBG") : null;
-        List<HashMap<String, Object>> JianChaBWList = ObjectUtils.isNotEmpty(JianChaBG) ? (List<HashMap<String, Object>>) JianChaBG.get("JianChaBWList") : null;
+        if (ObjectUtils.isEmpty(BingRenXX)) {
+            throw new BaseException("妫�鏌ユ姤鍛婂悓姝ュけ璐ワ細BingRenXX涓嶈兘涓虹┖");
+        }
+        if (ObjectUtils.isEmpty(JianChaBG)) {
+            throw new BaseException("妫�鏌ユ姤鍛婂悓姝ュけ璐ワ細JianChaBG涓嶈兘涓虹┖");
+        }
+
+        // 鏈烘瀯浼樺厛鍙栨秷鎭ご锛屽叾娆″彇鎮h�呬俊鎭紝鍜屾鏌ョ敵璇疯ˉ寤洪�昏緫淇濇寔涓�鑷�
+        String orgid = ObjectUtils.isNotEmpty(xiaoXiTou) ? getMapValue(xiaoXiTou, "FaSongJGID") : null;
+        if (StringUtils.isBlank(orgid)) {
+            orgid = getMapValue(BingRenXX, "ZuZhiJGID");
+        }
+        String bingAnHao = getMapValue(BingRenXX, "BingAnHao");
+        String bingRenID = getMapValue(BingRenXX, "BingRenID");
+        String xingMing = getMapValue(BingRenXX, "XingMing");
+        String shenQingDID = getMapValue(JianChaBG, "ShenQingDID");
+        if (StringUtils.isBlank(orgid) || StringUtils.isBlank(bingAnHao) || StringUtils.isBlank(bingRenID)
+                || StringUtils.isBlank(xingMing) || StringUtils.isBlank(shenQingDID)) {
+            throw new BaseException("妫�鏌ユ姤鍛婂悓姝ュけ璐ワ細BingRenXX.ZuZhiJGID/BingAnHao/BingRenID/XingMing銆丣ianChaBG.ShenQingDID涓嶈兘涓虹┖");
+        }
+
         PatMedInspection patMedInspection = new PatMedInspection();
         PatMedInspectionItem patMedInspectionItem = new PatMedInspectionItem();
-        patMedInspection.setOrgid(BingRenXX.get("ZuZhiJGID").toString());
+        patMedInspection.setOrgid(orgid);
         //涓芥按鏃犻櫌鍖猴紝榛樿鍊兼槸 1
         patMedInspection.setCampusid("1");
-        patMedInspection.setInhospno(BingRenXX.get("BingAnHao").toString());
-        patMedInspection.setPatidHis(BingRenXX.get("BingRenID").toString());
-        patMedInspection.setPatname(BingRenXX.get("XingMing").toString());
-        patMedInspection.setAppliyid(JianChaBG.get("ShenQingDID").toString());
+        patMedInspection.setInhospno(bingAnHao);
+        patMedInspection.setPatidHis(bingRenID);
+        patMedInspection.setPatname(xingMing);
+        patMedInspection.setAppliyid(shenQingDID);
         //鏌ユ壘鎮h�呮鏌ユ楠屼俊鎭紝濡傛灉鏃狅紝杩涜鏂板姞
         List<PatMedInspection> patMedInspections = patMedInspectionMapper.selectPatMedInspectionList(patMedInspection);
-        if (CollectionUtils.isNotEmpty(patMedInspections)) {
-            patMedInspectionItem.setInspectionid(patMedInspections.get(0).getId().intValue());
-        } else {
+        if (CollectionUtils.isEmpty(patMedInspections)) {
             addInspectService(dataMap);
-            List<PatMedInspection> patMedInspections2 = patMedInspectionMapper.selectPatMedInspectionList(patMedInspection);
-            patMedInspectionItem.setInspectionid(patMedInspections2.get(0).getId().intValue());
+            patMedInspections = patMedInspectionMapper.selectPatMedInspectionList(patMedInspection);
+            if (CollectionUtils.isEmpty(patMedInspections)) {
+                throw new BaseException("妫�鏌ユ姤鍛婂悓姝ュけ璐ワ細鏈壘鍒版垨鍒涘缓妫�鏌ヤ富璁板綍锛孲henQingDID=" + shenQingDID);
+            }
         }
+        patMedInspectionItem.setInspectionid(patMedInspections.get(0).getId().intValue());
         //妫�鏌ユ楠屾姤鍛婃暟鎹鐞�
-        patMedInspectionItem.setOrgid(BingRenXX.get("ZuZhiJGID").toString());
+        patMedInspectionItem.setOrgid(orgid);
         //涓芥按鏃犻櫌鍖猴紝榛樿鍊兼槸 1
         patMedInspectionItem.setCampusid("1");
         List<PatMedInspectionItem> patMedInspectionItems = patMedInspectionItemMapper.selectPatMedInspectionItemList(patMedInspectionItem);
-        if (ObjectUtils.isNotEmpty(JianChaBG)) {
-            patMedInspectionItem.setInspectionName(JianChaBG.get("JianChaHao").toString());
-            patMedInspectionItem.setReportid(JianChaBG.get("JianChaHao").toString());
-            patMedInspectionItem.setStatuscode(JianChaBG.get("DangQianZTDM").toString());
-            patMedInspectionItem.setStatusname(JianChaBG.get("DangQianZTMC").toString());
-            patMedInspectionItem.setReportid(JianChaBG.get("BaoGaoRID").toString());
-            patMedInspectionItem.setReportername(JianChaBG.get("BaoGaoRXM").toString());
-            patMedInspectionItem.setDiagimpression(JianChaBG.get("ZhenDuanYX").toString());
-            patMedInspectionItem.setDiagfindings(JianChaBG.get("ZhenDuanSJ").toString());
-            patMedInspectionItem.setDiagsuggestion(JianChaBG.get("ZhenDuanJY").toString());
-            patMedInspectionItem.setReporturl(JianChaBG.get("BaoGaoYXDZ").toString());
-            patMedInspectionItem.setCriticalFlag(Integer.valueOf(JianChaBG.get("WeiJiZBZ").toString()));
-            patMedInspectionItem.setCriticalContent(JianChaBG.get("WeiJiZNR").toString());
-            patMedInspectionItem.setReportdeptid(JianChaBG.get("BaoGaoKSID").toString());
-            patMedInspectionItem.setReportdeptname(JianChaBG.get("BaoGaoKSMC").toString());
-            patMedInspectionItem.setResultcode(JianChaBG.get("JianChaJGDM").toString());
-            patMedInspectionItem.setResultname(JianChaBG.get("JianChaJGMC").toString());
-            if (ObjectUtils.isNotEmpty(JianChaBG.get("BaoGaoSJ"))) {
-                try {
-                    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
-                    patMedInspection.setOpendate(simpleDateFormat.parse(JianChaBG.get("BaoGaoSJ").toString()));
-                } catch (ParseException e) {
-                    e.printStackTrace();
+
+        // 鍏煎 PACS 瀹為檯鎶ユ枃锛氭姤鍛婁汉銆佹姤鍛婃椂闂淬�佹姤鍛婂湴鍧�绛夊瓧娈靛悕涓庢爣鍑嗘姤鏂囧瓨鍦ㄥ樊寮�
+        String jianChaHao = getMapValue(JianChaBG, "JianChaHao");
+        String reportId = getMapValue(JianChaBG, "BaoGaoID");
+        if (StringUtils.isBlank(reportId)) {
+            reportId = getMapValue(JianChaBG, "BaoGaoRID");
+        }
+        if (StringUtils.isBlank(reportId)) {
+            reportId = jianChaHao;
+        }
+        String inspectionName = StringUtils.isNotBlank(jianChaHao) ? jianChaHao : reportId;
+        if (StringUtils.isBlank(inspectionName)) {
+            inspectionName = shenQingDID;
+        }
+        String reporterId = getMapValue(JianChaBG, "BaoGaoRID", "ZhiXingRID", "KaiDanRID");
+        String reporterName = getMapValue(JianChaBG, "BaoGaoRXM", "ZhiXingRXM", "KaiDanRXM");
+        String reportUrl = getMapValue(JianChaBG, "BaoGaoYXDZ", "TuWenBGXZDZ", "PDFurl");
+        String reportDateStr = getMapValue(JianChaBG, "BaoGaoSJ", "ZhiXingSJ");
+
+        patMedInspectionItem.setInspectionName(inspectionName);
+        patMedInspectionItem.setReportid(reportId);
+        patMedInspectionItem.setStatuscode(getMapValue(JianChaBG, "DangQianZTDM"));
+        patMedInspectionItem.setStatusname(getMapValue(JianChaBG, "DangQianZTMC"));
+        patMedInspectionItem.setReporterid(reporterId);
+        patMedInspectionItem.setReportername(reporterName);
+        patMedInspectionItem.setDiagimpression(getMapValue(JianChaBG, "ZhenDuanYX"));
+        patMedInspectionItem.setDiagfindings(getMapValue(JianChaBG, "ZhenDuanSJ"));
+        patMedInspectionItem.setDiagsuggestion(getMapValue(JianChaBG, "ZhenDuanJY"));
+        patMedInspectionItem.setReporturl(reportUrl);
+        patMedInspectionItem.setCriticalFlag(parseInteger(getMapValue(JianChaBG, "WeiJiZBZ")));
+        patMedInspectionItem.setCriticalContent(getMapValue(JianChaBG, "WeiJiZNR"));
+        patMedInspectionItem.setReportdeptid(getMapValue(JianChaBG, "BaoGaoKSID"));
+        patMedInspectionItem.setReportdeptname(getMapValue(JianChaBG, "BaoGaoKSMC"));
+        patMedInspectionItem.setResultcode(getMapValue(JianChaBG, "JianChaJGDM"));
+        patMedInspectionItem.setResultname(getMapValue(JianChaBG, "JianChaJGMC"));
+        Date reportDate = parseDateTime(reportDateStr);
+        if (reportDate != null) {
+            patMedInspectionItem.setReportdate(reportDate);
+        }
+
+        // 鍚屼竴妫�鏌ヤ笅鍙兘瀛樺湪澶氭潯鎶ュ憡鏄庣粏锛屼紭鍏堟寜鎶ュ憡ID銆佹鏌ュ彿鍖归厤锛岄伩鍏嶆洿鏂伴敊琛�
+        PatMedInspectionItem existingItem = null;
+        if (CollectionUtils.isNotEmpty(patMedInspectionItems)) {
+            for (PatMedInspectionItem item : patMedInspectionItems) {
+                if (StringUtils.isNotBlank(reportId) && reportId.equals(item.getReportid())) {
+                    existingItem = item;
+                    break;
                 }
             }
+            if (existingItem == null) {
+                for (PatMedInspectionItem item : patMedInspectionItems) {
+                    if (StringUtils.isNotBlank(inspectionName) && inspectionName.equals(item.getInspectionName())) {
+                        existingItem = item;
+                        break;
+                    }
+                }
+            }
+            if (existingItem == null) {
+                existingItem = patMedInspectionItems.get(0);
+            }
         }
-        if (ObjectUtils.isNotEmpty(patMedInspectionItems)) {
+
+        if (existingItem != null) {
+            // 鏇存柊蹇呴』甯︿笂涓婚敭锛屽惁鍒� update ... where id = null 浼氬奖鍝� 0 琛岋紝鎺ュ彛杩斿洖澶辫触
+            patMedInspectionItem.setId(existingItem.getId());
             patMedInspectionItem.setUpdateTime(new Date());
             int i = patMedInspectionItemMapper.updatePatMedInspectionItem(patMedInspectionItem);
-            if (i != 1) return false;
+            if (i < 1) {
+                throw new BaseException("妫�鏌ユ姤鍛婃槑缁嗘洿鏂板け璐ワ紝id=" + existingItem.getId());
+            }
         } else {
             patMedInspectionItem.setDelFlag("0");
             patMedInspectionItem.setCreateTime(new Date());
             int i = patMedInspectionItemMapper.insertPatMedInspectionItem(patMedInspectionItem);
-            if (i != 1) return false;
+            if (i != 1) {
+                throw new BaseException("妫�鏌ユ姤鍛婃槑缁嗘柊澧炲け璐ワ紝ShenQingDID=" + shenQingDID);
+            }
         }
 
         return true;
+    }
+
+    /**
+     * 浠庢姤鏂囦腑鎸夐『搴忚幏鍙栫涓�涓潪绌哄瓧娈碉紝鍏煎涓嶅悓鍘傚晢鐨勫瓧娈靛懡鍚�
+     */
+    private String getMapValue(Map<String, Object> source, String... keys) {
+        if (source == null || keys == null) {
+            return null;
+        }
+        for (String key : keys) {
+            Object value = source.get(key);
+            if (value != null && StringUtils.isNotBlank(String.valueOf(value))) {
+                return String.valueOf(value);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 瑙f瀽妫�鏌ユ姤鍛婃椂闂达紝鍏煎甯歌鏃ユ湡鏃堕棿鏍煎紡
+     */
+    private Date parseDateTime(String dateTime) {
+        if (StringUtils.isBlank(dateTime)) {
+            return null;
+        }
+        String[] patterns = {"yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd'T'HH:mm:ss", "yyyy-MM-dd"};
+        for (String pattern : patterns) {
+            try {
+                SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
+                simpleDateFormat.setLenient(false);
+                return simpleDateFormat.parse(dateTime);
+            } catch (ParseException ignored) {
+                // 灏濊瘯涓嬩竴绉嶆牸寮�
+            }
+        }
+        log.warn("妫�鏌ユ姤鍛婃椂闂存牸寮忔棤娉曡В鏋愶細{}", dateTime);
+        return null;
+    }
+
+    /**
+     * 瑙f瀽妫�鏌ユ姤鍛婃暟瀛楀瓧娈碉紝閬垮厤闈炴暟瀛楀唴瀹瑰鑷存帴鍙f姤閿�
+     */
+    private Integer parseInteger(String value) {
+        if (StringUtils.isBlank(value)) {
+            return null;
+        }
+        try {
+            return Integer.valueOf(value.trim());
+        } catch (NumberFormatException e) {
+            log.warn("妫�鏌ユ姤鍛婃暟瀛楀瓧娈垫棤娉曡浆鎹細{}", value);
+            return null;
+        }
     }
 
     @Override
@@ -2348,4 +2479,100 @@
         }
         return 1;
     }
+
+    //鏂板鎮h�呬笓绉戞。妗堝熀鏈俊鎭�
+    private void genePatSpecificArchive(PatMedOuthosp patMedOuthosp, PatArchive patArchive) {
+        String deptCategory = judgeDeptCategory(patMedOuthosp.getDeptcode(), patMedOuthosp.getOrgid());
+
+        boolean isGynecology = "1".equals(deptCategory);  // 鏄惁濡囩
+        boolean isPediatrics = "2".equals(deptCategory);  // 鏄惁鍎跨
+
+        if (isGynecology) {
+            // 璧板绉戞。妗堢敓鎴愰�昏緫
+            PatMaternityArchiveDetail patMaternityArchiveDetail
+                    = DtoConversionUtils.sourceToTarget(patMedOuthosp, PatMaternityArchiveDetail.class);
+            patMaternityArchiveDetail.setSourceType("1");
+            patMaternityArchiveDetail.setPhone(patArchive.getTelcode());
+            patMaternityArchiveDetail.setPatid(patArchive.getId());
+            patMaternityArchiveDetail.setPatname(patArchive.getName());
+            patMaternityArchiveDetail.setBirthdate(patArchive.getBirthdate());
+            patMaternityArchiveDetail.setSex(patArchive.getSex());
+            patMaternityArchiveDetail.setAddress(patArchive.getPlaceOfResidence());
+            patMaternityArchiveDetail.setOuthospno(patMedOuthosp.getOuthospno());
+            patMaternityArchiveDetail.setSerialnum(patMedOuthosp.getSerialnum());
+            patMaternityArchiveDetail.setIcd10code(patMedOuthosp.getIcd10code());
+            patMaternityArchiveDetail.setDeptcode(patMedOuthosp.getDeptcode());
+            patMaternityArchiveDetail.setDeptname(patMedOuthosp.getDeptname());
+            patMaternityArchiveDetail.setDoctorName(patMedOuthosp.getDrname());
+            patMaternityArchiveDetail.setDoctorCode(patMedOuthosp.getDrcode());
+            patMaternityArchiveDetail.setManagementDoctorName(patMedOuthosp.getDrname());
+            patMaternityArchiveDetail.setManagementDoctorCode(patMedOuthosp.getDrcode());
+            patMaternityArchiveDetail.setOrgid(patMedOuthosp.getOrgid());
+            patMaternityArchiveDetail.setCampusid(patMedOuthosp.getCampusid());
+            patMaternityArchiveService.insertOrUpdateMaternityArchive(patMaternityArchiveDetail);
+        } else if (isPediatrics) {
+            // 璧板効绉戞。妗堢敓鎴愰�昏緫
+            PatChildArchiveDetail patChildArchiveDetail
+                    = DtoConversionUtils.sourceToTarget(patMedOuthosp, PatChildArchiveDetail.class);
+            patChildArchiveDetail.setSourceType("1");
+            patChildArchiveDetail.setPhone(patArchive.getTelcode());
+            patChildArchiveDetail.setPatid(patArchive.getId());
+            patChildArchiveDetail.setPatname(patArchive.getName());
+            patChildArchiveDetail.setBirthdate(patArchive.getBirthdate());
+            patChildArchiveDetail.setSex(patArchive.getSex());
+//            patChildArchiveDetail.setAddress(patArchive.getPlaceOfResidence());
+            patChildArchiveDetail.setOuthospno(patMedOuthosp.getOuthospno());
+            patChildArchiveDetail.setSerialnum(patMedOuthosp.getSerialnum());
+            patChildArchiveDetail.setIcd10code(patMedOuthosp.getIcd10code());
+            patChildArchiveDetail.setDeptcode(patMedOuthosp.getDeptcode());
+            patChildArchiveDetail.setDeptname(patMedOuthosp.getDeptname());
+            patChildArchiveDetail.setDoctorName(patMedOuthosp.getDrname());
+            patChildArchiveDetail.setDoctorCode(patMedOuthosp.getDrcode());
+            patChildArchiveDetail.setManagementDoctorName(patMedOuthosp.getDrname());
+            patChildArchiveDetail.setManagementDoctorCode(patMedOuthosp.getDrcode());
+            patChildArchiveDetail.setOrgid(patMedOuthosp.getOrgid());
+            patChildArchiveDetail.setCampusid(patMedOuthosp.getCampusid());
+            patChildArchiveService.insertOrUpdateChildArchive(patChildArchiveDetail);
+        } else {
+            // 鏈懡涓厤缃紝涓嶇敓鎴愭。妗�
+        }
+    }
+
+    /**
+     * 鍒ゆ柇褰撳墠绉戝灞炰簬濡囩杩樻槸鍎跨
+     *
+     * @param deptCode 褰撳墠绉戝/鐥呭尯 Code锛屽 "D004"
+     * @param orgid    鍖婚櫌鏈烘瀯ID
+     * @return deptCategory锛�1-濡囩 2-鍎跨锛沶ull-鏈厤缃垨鏈懡涓�
+     */
+    public String judgeDeptCategory(String deptCode, String orgid) {
+        if (StringUtils.isEmpty(deptCode)) {
+            return null;
+        }
+
+        // 浼樺厛鍙栨満鏋勭骇閰嶇疆锛屽彇涓嶅埌鍐嶅彇鍏ㄥ眬閰嶇疆
+        String configValue = configService.selectConfigByKey("sys.dept.category",
+                orgid);
+        if (StringUtils.isEmpty(configValue)) {
+            configValue = configService.selectConfigByKey("sys.dept.category");
+        }
+        if (StringUtils.isEmpty(configValue)) {
+            return null;
+        }
+
+        JSONArray configs = JSON.parseArray(configValue);
+        if (configs == null || configs.isEmpty()) {
+            return null;
+        }
+
+        for (int i = 0; i < configs.size(); i++) {
+            JSONObject cfg = configs.getJSONObject(i);
+            JSONArray deptCodes = cfg.getJSONArray("deptCodes");
+            if (deptCodes != null && deptCodes.contains(deptCode)) {
+                return cfg.getString("deptCategory");
+            }
+        }
+        return null;
+    }
+
 }
diff --git a/smartor/src/main/java/com/smartor/service/impl/SvyLibTemplateScriptServiceImpl.java b/smartor/src/main/java/com/smartor/service/impl/SvyLibTemplateScriptServiceImpl.java
index 3947ded..3061511 100644
--- a/smartor/src/main/java/com/smartor/service/impl/SvyLibTemplateScriptServiceImpl.java
+++ b/smartor/src/main/java/com/smartor/service/impl/SvyLibTemplateScriptServiceImpl.java
@@ -15,6 +15,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 
 import com.ruoyi.template.domain.SvyLibTemplateScript;
 import com.ruoyi.template.domain.SvyLibTemplateTargetoption;
@@ -149,25 +150,28 @@
                     }
                 }
                 itemCounts.forEach(itemCount ->{
-                    if(ObjectUtils.isNotEmpty(count.getScriptid()) && ObjectUtils.isNotEmpty(itemCount.getScriptid())){
-                        if(count.getScriptid().equals(itemCount.getScriptid())){
-                            //鏀惧叆缁熻淇℃伅
-                            itemCount.setScriptContent(count.getScriptContent());
-                            itemCount.setScriptType(count.getScriptType());
-                            itemCount.setSubtaskType(count.getSubtaskType());
-                            itemCount.setCompletedQuantity(count.getCompletedQuantity());
-                            itemCount.setAllQuantity(count.getAllQuantity());
-                            itemCount.setCompletedPercentage(count.getCompletedPercentage());
-                            itemCount.setTemplateid(count.getTemplateid());
-                            //璁$畻閫夋嫨姣斾緥
-                            int chosenQuantity = ObjectUtils.isNotEmpty(itemCount.getChosenQuantity())?itemCount.getChosenQuantity():0;
-                            int completedQuantity = itemCount.getCompletedQuantity();
-                            if(ObjectUtils.isNotEmpty(completedQuantity) && completedQuantity != 0){
-                                BigDecimal chosenPercentage = (new BigDecimal(chosenQuantity)).divide(new BigDecimal(completedQuantity), 2, RoundingMode.HALF_UP);
-                                itemCount.setChosenPercentage(chosenPercentage.toString());
-                            }
-                            itemCountMap.put(itemCount.getOptionText(), itemCount);
+                    //鏄庣粏琛屽凡鎸夆�滈鐩唴瀹�+浠诲姟绫诲瀷鈥濊仛鍚堬紝涓嶈兘鐢� scriptid 鍖归厤锛堝悓涓�閬撻鍦ㄦ瘡涓换鍔℃ā鏉块噷鐨� scriptid 閮戒笉鍚岋級
+                    boolean sameQuestion = ObjectUtils.isNotEmpty(count.getScriptContent())
+                            && ObjectUtils.isNotEmpty(itemCount.getScriptContent())
+                            && count.getScriptContent().equals(itemCount.getScriptContent())
+                            && Objects.equals(count.getSubtaskType(), itemCount.getSubtaskType());
+                    if(sameQuestion){
+                        //鏀惧叆缁熻淇℃伅
+                        itemCount.setScriptContent(count.getScriptContent());
+                        itemCount.setScriptType(count.getScriptType());
+                        itemCount.setSubtaskType(count.getSubtaskType());
+                        itemCount.setCompletedQuantity(count.getCompletedQuantity());
+                        itemCount.setAllQuantity(count.getAllQuantity());
+                        itemCount.setCompletedPercentage(count.getCompletedPercentage());
+                        itemCount.setTemplateid(count.getTemplateid());
+                        //璁$畻閫夋嫨姣斾緥
+                        int chosenQuantity = ObjectUtils.isNotEmpty(itemCount.getChosenQuantity())?itemCount.getChosenQuantity():0;
+                        int completedQuantity = itemCount.getCompletedQuantity();
+                        if(ObjectUtils.isNotEmpty(completedQuantity) && completedQuantity != 0){
+                            BigDecimal chosenPercentage = (new BigDecimal(chosenQuantity)).divide(new BigDecimal(completedQuantity), 2, RoundingMode.HALF_UP);
+                            itemCount.setChosenPercentage(chosenPercentage.toString());
                         }
+                        itemCountMap.put(itemCount.getOptionText(), itemCount);
                     }
                 });
                 List<ServiceSubtaskScriptCount> details = new ArrayList<>(itemCountMap.values());
diff --git a/smartor/src/main/resources/mapper/smartor/PatChildArchiveDetailMapper.xml b/smartor/src/main/resources/mapper/smartor/PatChildArchiveDetailMapper.xml
index 591f495..d17a477 100644
--- a/smartor/src/main/resources/mapper/smartor/PatChildArchiveDetailMapper.xml
+++ b/smartor/src/main/resources/mapper/smartor/PatChildArchiveDetailMapper.xml
@@ -174,6 +174,7 @@
     <select id="selectPatChildArchiveDetailById" parameterType="Long" resultMap="PatChildArchiveDetailResult">
         <include refid="selectPatChildArchiveDetailVo"/>
         where id = #{id}
+        and del_flag = '0'
     </select>
 
     <insert id="insertPatChildArchiveDetail" parameterType="com.smartor.domain.PatChildArchiveDetail"
@@ -374,4 +375,11 @@
             #{id}
         </foreach>
     </update>
+
+    <update id="deletePatChildArchiveDetailByArchiveIds" parameterType="String">
+        update pat_child_archive_detail set del_flag = '1' where archiveid in
+        <foreach item="archiveId" collection="array" open="(" separator="," close=")">
+            #{archiveId}
+        </foreach>
+    </update>
 </mapper>
diff --git a/smartor/src/main/resources/mapper/smartor/PatChildArchiveMapper.xml b/smartor/src/main/resources/mapper/smartor/PatChildArchiveMapper.xml
index 4092c18..95fb901 100644
--- a/smartor/src/main/resources/mapper/smartor/PatChildArchiveMapper.xml
+++ b/smartor/src/main/resources/mapper/smartor/PatChildArchiveMapper.xml
@@ -19,7 +19,6 @@
         <result property="diagTime" column="diag_time"/>
         <result property="archiveTime" column="archive_time"/>
         <result property="plannedFollowTime" column="planned_follow_time"/>
-        <result property="followupCycleTimes" column="followup_cycle_times"/>
         <result property="sex" column="sex"/>
         <result property="doctorCode" column="doctor_code"/>
         <result property="doctorName" column="doctor_name"/>
@@ -31,7 +30,14 @@
         <result property="wardname" column="wardname"/>
         <result property="icd10code" column="icd10code"/>
         <result property="diagname" column="diagname"/>
+        <result property="appointmentSituation" column="appointment_situation"/>
         <result property="closeStatus" column="close_status"/>
+        <result property="nutritionIcd10Code" column="nutrition_icd10_code"/>
+        <result property="nutritionDiagName" column="nutrition_diag_name"/>
+        <result property="gestationalWeek" column="gestational_week"/>
+        <result property="birthWeight" column="birth_weight"/>
+        <result property="highRiskChildType" column="high_risk_child_type"/>
+        <result property="highRiskChildName" column="high_risk_child_name"/>
         <result property="registerUser" column="register_user"/>
         <result property="remark" column="remark"/>
         <result property="orgid" column="orgid"/>
@@ -58,7 +64,6 @@
                diag_time,
                archive_time,
                planned_follow_time,
-               followup_cycle_times,
                sex,
                doctor_code,
                doctor_name,
@@ -70,7 +75,14 @@
                wardname,
                icd10code,
                diagname,
+               appointment_situation,
                close_status,
+               nutrition_icd10_code,
+               nutrition_diag_name,
+               gestational_week,
+               birth_weight,
+               high_risk_child_type,
+               high_risk_child_name,
                register_user,
                remark,
                orgid,
@@ -100,7 +112,6 @@
             <if test="phone != null and phone != ''">and phone = #{phone}</if>
             <if test="diagTime != null">and diag_time = #{diagTime}</if>
             <if test="plannedFollowTime != null">and planned_follow_time = #{plannedFollowTime}</if>
-            <if test="followupCycleTimes != null and followupCycleTimes != ''">and followup_cycle_times = #{followupCycleTimes}</if>
             <if test="sex != null">and sex = #{sex}</if>
             <if test="doctorCode != null and doctorCode != ''">and doctor_code = #{doctorCode}</if>
             <if test="doctorName != null and doctorName != ''">and doctor_name like concat('%', #{doctorName}, '%')</if>
@@ -112,7 +123,13 @@
             <if test="wardname != null and wardname != ''">and wardname like concat('%', #{wardname}, '%')</if>
             <if test="icd10code != null and icd10code != ''">and icd10code = #{icd10code}</if>
             <if test="diagname != null and diagname != ''">and diagname like concat('%', #{diagname}, '%')</if>
+            <if test="nutritionIcd10Code != null and nutritionIcd10Code != ''">and nutrition_icd10_code = #{nutritionIcd10Code}</if>
+            <if test="nutritionDiagName != null and nutritionDiagName != ''">and nutrition_diag_name like concat('%', #{nutritionDiagName}, '%')</if>
+            <if test="highRiskChildType != null and highRiskChildType != ''">and high_risk_child_type = #{highRiskChildType}</if>
+            <if test="highRiskChildName != null and highRiskChildName != ''">and high_risk_child_name like concat('%', #{highRiskChildName}, '%')</if>
             <if test="closeStatus != null and closeStatus != ''">and close_status = #{closeStatus}</if>
+            <if test="gestationalWeek != null">and gestational_week = #{gestationalWeek}</if>
+            <if test="birthWeight != null">and birth_weight = #{birthWeight}</if>
             <if test="registerUser != null and registerUser != ''">and register_user = #{registerUser}</if>
             <if test="orgid != null and orgid != ''">and orgid = #{orgid}</if>
             <if test="campusid != null and campusid != ''">and campusid = #{campusid}</if>
@@ -127,6 +144,7 @@
     <select id="selectPatChildArchiveById" parameterType="Long" resultMap="PatChildArchiveResult">
         <include refid="selectPatChildArchiveVo"/>
         where id = #{id}
+        and del_flag = '0'
     </select>
 
     <insert id="insertPatChildArchive" parameterType="com.smartor.domain.PatChildArchive"
@@ -146,7 +164,6 @@
             <if test="diagTime != null">diag_time,</if>
             <if test="archiveTime != null">archive_time,</if>
             <if test="plannedFollowTime != null">planned_follow_time,</if>
-            <if test="followupCycleTimes != null">followup_cycle_times,</if>
             <if test="sex != null">sex,</if>
             <if test="doctorCode != null">doctor_code,</if>
             <if test="doctorName != null">doctor_name,</if>
@@ -158,7 +175,14 @@
             <if test="wardname != null">wardname,</if>
             <if test="icd10code != null">icd10code,</if>
             <if test="diagname != null">diagname,</if>
+            <if test="appointmentSituation != null">appointment_situation,</if>
             <if test="closeStatus != null">close_status,</if>
+            <if test="nutritionIcd10Code != null">nutrition_icd10_code,</if>
+            <if test="nutritionDiagName != null">nutrition_diag_name,</if>
+            <if test="gestationalWeek != null">gestational_week,</if>
+            <if test="birthWeight != null">birth_weight,</if>
+            <if test="highRiskChildType != null">high_risk_child_type,</if>
+            <if test="highRiskChildName != null">high_risk_child_name,</if>
             <if test="registerUser != null">register_user,</if>
             <if test="remark != null">remark,</if>
             <if test="orgid != null">orgid,</if>
@@ -183,7 +207,6 @@
             <if test="diagTime != null">#{diagTime},</if>
             <if test="archiveTime != null">#{archiveTime},</if>
             <if test="plannedFollowTime != null">#{plannedFollowTime},</if>
-            <if test="followupCycleTimes != null">#{followupCycleTimes},</if>
             <if test="sex != null">#{sex},</if>
             <if test="doctorCode != null">#{doctorCode},</if>
             <if test="doctorName != null">#{doctorName},</if>
@@ -195,7 +218,14 @@
             <if test="wardname != null">#{wardname},</if>
             <if test="icd10code != null">#{icd10code},</if>
             <if test="diagname != null">#{diagname},</if>
+            <if test="appointmentSituation != null">#{appointmentSituation},</if>
             <if test="closeStatus != null">#{closeStatus},</if>
+            <if test="nutritionIcd10Code != null">#{nutritionIcd10Code},</if>
+            <if test="nutritionDiagName != null">#{nutritionDiagName},</if>
+            <if test="gestationalWeek != null">#{gestationalWeek},</if>
+            <if test="birthWeight != null">#{birthWeight},</if>
+            <if test="highRiskChildType != null">#{highRiskChildType},</if>
+            <if test="highRiskChildName != null">#{highRiskChildName},</if>
             <if test="registerUser != null">#{registerUser},</if>
             <if test="remark != null">#{remark},</if>
             <if test="orgid != null">#{orgid},</if>
@@ -224,7 +254,6 @@
             <if test="diagTime != null">diag_time = #{diagTime},</if>
             <if test="archiveTime != null">archive_time = #{archiveTime},</if>
             <if test="plannedFollowTime != null">planned_follow_time = #{plannedFollowTime},</if>
-            <if test="followupCycleTimes != null">followup_cycle_times = #{followupCycleTimes},</if>
             <if test="sex != null">sex = #{sex},</if>
             <if test="doctorCode != null">doctor_code = #{doctorCode},</if>
             <if test="doctorName != null">doctor_name = #{doctorName},</if>
@@ -236,7 +265,14 @@
             <if test="wardname != null">wardname = #{wardname},</if>
             <if test="icd10code != null">icd10code = #{icd10code},</if>
             <if test="diagname != null">diagname = #{diagname},</if>
+            <if test="appointmentSituation != null">appointment_situation = #{appointmentSituation},</if>
             <if test="closeStatus != null">close_status = #{closeStatus},</if>
+            <if test="nutritionIcd10Code != null">nutrition_icd10_code = #{nutritionIcd10Code},</if>
+            <if test="nutritionDiagName != null">nutrition_diag_name = #{nutritionDiagName},</if>
+            <if test="gestationalWeek != null">gestational_week = #{gestationalWeek},</if>
+            <if test="birthWeight != null">birth_weight = #{birthWeight},</if>
+            <if test="highRiskChildType != null">high_risk_child_type = #{highRiskChildType},</if>
+            <if test="highRiskChildName != null">high_risk_child_name = #{highRiskChildName},</if>
             <if test="registerUser != null">register_user = #{registerUser},</if>
             <if test="remark != null">remark = #{remark},</if>
             <if test="orgid != null">orgid = #{orgid},</if>
diff --git a/smartor/src/main/resources/mapper/smartor/PatMaternityArchiveDetailMapper.xml b/smartor/src/main/resources/mapper/smartor/PatMaternityArchiveDetailMapper.xml
index 731cdac..fb2fef3 100644
--- a/smartor/src/main/resources/mapper/smartor/PatMaternityArchiveDetailMapper.xml
+++ b/smartor/src/main/resources/mapper/smartor/PatMaternityArchiveDetailMapper.xml
@@ -39,6 +39,12 @@
         <result property="dueDate" column="due_date"/>
         <result property="registerUser" column="register_user"/>
         <result property="followupPeriod" column="followup_period"/>
+        <result property="followupCycleTimes" column="followup_cycle_times"/>
+        <result property="lastMenstrualTime" column="last_menstrual_time"/>
+        <result property="firstDoctorCode" column="first_doctor_code"/>
+        <result property="firstDoctorName" column="first_doctor_name"/>
+        <result property="contactPhone" column="contact_phone"/>
+        <result property="address" column="address"/>
         <result property="epEarlyPregnancyFollowupForm" column="ep_early_pregnancy_followup_form"/>
         <result property="mhiMenopauseFollowupSystemIssuesForm" column="mhi_menopause_followup_system_issues_form"/>
         <result property="mhiMhtInitialVisitMedicalRecordForm" column="mhi_mht_initial_visit_medical_record_form"/>
@@ -91,6 +97,12 @@
                due_date,
                register_user,
                followup_period,
+               followup_cycle_times,
+               last_menstrual_time,
+               first_doctor_code,
+               first_doctor_name,
+               contact_phone,
+               address,
                ep_early_pregnancy_followup_form,
                mhi_menopause_followup_system_issues_form,
                mhi_mht_initial_visit_medical_record_form,
@@ -152,6 +164,7 @@
     <select id="selectPatMaternityArchiveDetailById" parameterType="Long" resultMap="PatMaternityArchiveDetailResult">
         <include refid="selectPatMaternityArchiveDetailVo"/>
         where id = #{id}
+        and del_flag = '0'
     </select>
 
     <insert id="insertPatMaternityArchiveDetail" parameterType="com.smartor.domain.PatMaternityArchiveDetail"
@@ -191,6 +204,12 @@
             <if test="dueDate != null">due_date,</if>
             <if test="registerUser != null">register_user,</if>
             <if test="followupPeriod != null">followup_period,</if>
+            <if test="followupCycleTimes != null">followup_cycle_times,</if>
+            <if test="lastMenstrualTime != null">last_menstrual_time,</if>
+            <if test="firstDoctorCode != null">first_doctor_code,</if>
+            <if test="firstDoctorName != null">first_doctor_name,</if>
+            <if test="contactPhone != null">contact_phone,</if>
+            <if test="address != null">address,</if>
             <if test="epEarlyPregnancyFollowupForm != null">ep_early_pregnancy_followup_form,</if>
             <if test="mhiMenopauseFollowupSystemIssuesForm != null">mhi_menopause_followup_system_issues_form,</if>
             <if test="mhiMhtInitialVisitMedicalRecordForm != null">mhi_mht_initial_visit_medical_record_form,</if>
@@ -241,6 +260,12 @@
             <if test="dueDate != null">#{dueDate},</if>
             <if test="registerUser != null">#{registerUser},</if>
             <if test="followupPeriod != null">#{followupPeriod},</if>
+            <if test="followupCycleTimes != null">#{followupCycleTimes},</if>
+            <if test="lastMenstrualTime != null">#{lastMenstrualTime},</if>
+            <if test="firstDoctorCode != null">#{firstDoctorCode},</if>
+            <if test="firstDoctorName != null">#{firstDoctorName},</if>
+            <if test="contactPhone != null">#{contactPhone},</if>
+            <if test="address != null">#{address},</if>
             <if test="epEarlyPregnancyFollowupForm != null">#{epEarlyPregnancyFollowupForm},</if>
             <if test="mhiMenopauseFollowupSystemIssuesForm != null">#{mhiMenopauseFollowupSystemIssuesForm},</if>
             <if test="mhiMhtInitialVisitMedicalRecordForm != null">#{mhiMhtInitialVisitMedicalRecordForm},</if>
@@ -295,6 +320,12 @@
             <if test="dueDate != null">due_date = #{dueDate},</if>
             <if test="registerUser != null">register_user = #{registerUser},</if>
             <if test="followupPeriod != null">followup_period = #{followupPeriod},</if>
+            <if test="followupCycleTimes != null">followup_cycle_times = #{followupCycleTimes},</if>
+            <if test="lastMenstrualTime != null">last_menstrual_time = #{lastMenstrualTime},</if>
+            <if test="firstDoctorCode != null">first_doctor_code = #{firstDoctorCode},</if>
+            <if test="firstDoctorName != null">first_doctor_name = #{firstDoctorName},</if>
+            <if test="contactPhone != null">contact_phone = #{contactPhone},</if>
+            <if test="address != null">address = #{address},</if>
             <if test="epEarlyPregnancyFollowupForm != null">ep_early_pregnancy_followup_form = #{epEarlyPregnancyFollowupForm},</if>
             <if test="mhiMenopauseFollowupSystemIssuesForm != null">mhi_menopause_followup_system_issues_form = #{mhiMenopauseFollowupSystemIssuesForm},</if>
             <if test="mhiMhtInitialVisitMedicalRecordForm != null">mhi_mht_initial_visit_medical_record_form = #{mhiMhtInitialVisitMedicalRecordForm},</if>
@@ -328,4 +359,11 @@
             #{id}
         </foreach>
     </update>
+
+    <update id="deletePatMaternityArchiveDetailByArchiveIds" parameterType="String">
+        update pat_maternity_archive_detail set del_flag = '1' where archiveid in
+        <foreach item="archiveId" collection="array" open="(" separator="," close=")">
+            #{archiveId}
+        </foreach>
+    </update>
 </mapper>
diff --git a/smartor/src/main/resources/mapper/smartor/PatMaternityArchiveMapper.xml b/smartor/src/main/resources/mapper/smartor/PatMaternityArchiveMapper.xml
index dac32bf..194b85f 100644
--- a/smartor/src/main/resources/mapper/smartor/PatMaternityArchiveMapper.xml
+++ b/smartor/src/main/resources/mapper/smartor/PatMaternityArchiveMapper.xml
@@ -130,6 +130,7 @@
     <select id="selectPatMaternityArchiveById" parameterType="Long" resultMap="PatMaternityArchiveResult">
         <include refid="selectPatMaternityArchiveVo"/>
         where id = #{id}
+        and del_flag = '0'
     </select>
 
     <insert id="insertPatMaternityArchive" parameterType="com.smartor.domain.PatMaternityArchive"

--
Gitblit v1.9.3