From d9281d43e27ab6c510d390ddb26eaffc8ccc1add Mon Sep 17 00:00:00 2001
From: 陈昶聿 <chychen@nbjetron.com>
Date: 星期三, 11 十一月 2026 14:04:52 +0800
Subject: [PATCH] 妇幼-补充住院随访档案生成日志、完成事件及自检文件

---
 ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceSubtaskServiceImpl.java | 1302 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 1,248 insertions(+), 54 deletions(-)

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..3ed3fd6 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,10 @@
 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;
+import com.ruoyi.task.event.FollowupCompletedEventPublisher;
 
 /**
  * 鍗曚竴浠诲姟锛堥殢璁匡級Service涓氬姟灞傚鐞�
@@ -133,6 +137,18 @@
 @Slf4j
 @Service
 public class ServiceSubtaskServiceImpl implements IServiceSubtaskService {
+
+    /**
+     * 闅忚瀛愪换鍔$粍瑁呭櫒锛堝師 smartor 渚� IPatMedInhospService#boxedServiceSubtask 鍦ㄦ湰妯″潡鐨勮惤鍦板疄鐜帮級銆�
+     * <p>
+     * 浜屾闅忚瑕佺敤銆屼笅涓�浠诲姟閰嶇疆 + 鍑洪櫌璁板綍 + 鎮h�呮。妗堛�嶇粍瑁呮柊鐨� service_subtask锛�
+     * 杩欐鍙e緞涓庨暱鏈熶换鍔$敓鎴愬畬鍏ㄤ竴鑷达紝鍥犳鐩存帴澶嶇敤鏈ā鍧楀凡鏈夌殑缁勮鍣紝涓嶅啀澶嶅埗涓�浠姐��
+     */
+    @Autowired
+    private OperationSubtaskWriter operationSubtaskWriter;
+
+    @Autowired
+    private FollowupCompletedEventPublisher followupCompletedEventPublisher;
 
     /**
      * 婊℃剰搴︽ā鍧楀澶栭棬闈紙甯搁┗ Bean锛夈��
@@ -218,6 +234,9 @@
     private TaskArchivePort taskArchivePort;
 
     @Autowired
+    private ISysDeptService sysDeptService;
+
+    @Autowired
     private ScheduleSubtaskBuilder scheduleSubtaskBuilder;
 
 
@@ -260,6 +279,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 +508,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 +714,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 +799,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 +836,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);
@@ -881,7 +917,7 @@
             serviceOutPath.setSubId(serviceSubtask1.getId());
             List<ServiceOutPath> serviceOutPaths = serviceOutPathMapper.selectServiceOutPathList(serviceOutPath);
             if (CollectionUtils.isNotEmpty(serviceOutPaths))
-                format = String.format("%03X", serviceOutPaths.get(0).getId());
+                format = ShortCode.encode(serviceOutPaths.get(0).getId());
             else format = getOutPath(serviceSubtask1, rsaPublicKeyExample);
             serviceSubtask = serviceSubtask1;
         } else {
@@ -933,7 +969,7 @@
         serviceOutPath.setSubId(serviceSubtask.getId());
         iServiceOutPathService.insertServiceOutPath(serviceOutPath);
         //杞垚16杩涘埗
-        format = String.format("%03X", serviceOutPath.getId());
+        format = ShortCode.encode(serviceOutPath.getId());
         serviceOutPath.setRadix(format);
         serviceOutPath.setUpdateTime(new Date());
         iServiceOutPathService.updateServiceOutPath(serviceOutPath);
@@ -947,6 +983,7 @@
      * @return 缁撴灉
      */
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public String addSubTaskAgain(ServiceSubtask serviceSubtask) {
         ServiceSubtask serviceSubtask1 = selectServiceSubtaskById(serviceSubtask.getId());
         PatMedInhosp patMedInhosp = new PatMedInhosp();
@@ -956,7 +993,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 +1007,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);
@@ -1006,6 +1047,495 @@
         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();
+            applyAgainVisitTime(serviceSubtaskAgain, serviceSubtask, nextServiceTask, config, now);
+            // 鍖荤敓澶勭悊鎰忚鍙栬嚜鏈璇锋眰锛屾寜涓嬩竴浠诲姟閲嶅缓瀛愪换鍔℃椂闇�鏄惧紡甯﹀叆锛屽惁鍒欎笉浼氳惤搴�
+            if (request != null) {
+                serviceSubtaskAgain.setDoctorOpinion(request.getDoctorOpinion());
+                serviceSubtaskAgain.setRemark(request.getRemark());
+                serviceSubtaskAgain.setDrcode(request.getDrcode());
+                serviceSubtaskAgain.setDrname(request.getDrname());
+                serviceSubtaskAgain.setNurseId(request.getNurseId());
+                serviceSubtaskAgain.setNurseName(request.getNurseName());
+                serviceSubtaskAgain.setManagementDoctor(request.getManagementDoctor());
+                serviceSubtaskAgain.setManagementDoctorCode(request.getManagementDoctorCode());
+            }
+            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);
+            // 鏂颁换鍔$殑闅忚鏂瑰紡璁板綍鏈閫夋嫨鐨勫啀娆¢殢璁挎柟寮忥紱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;
+        }
+        if(ObjectUtils.isNotEmpty(origin.getVisitTime())){
+            dueTime = ObjectUtils.isNotEmpty(origin.getVisitTime()) ? origin.getVisitTime() : 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;
+    }
+
     /**
      * 淇敼鍗曚竴浠诲姟锛堥殢璁匡級
      *
@@ -1018,7 +1548,12 @@
         if (serviceSubtask.getSendstate() != null && serviceSubtask.getSendstate() == 6)
             serviceSubtask.setFinishtime(new Date());
 
-        return serviceSubtaskMapper.updateServiceSubtask(serviceSubtask);
+        Boolean updated = serviceSubtaskMapper.updateServiceSubtask(serviceSubtask);
+        if (Boolean.TRUE.equals(updated) && serviceSubtask.getSendstate() != null
+                && serviceSubtask.getSendstate() == 6 && serviceSubtask.getId() != null) {
+            followupCompletedEventPublisher.publish(serviceSubtask.getId());
+        }
+        return updated;
     }
 
     @Override
@@ -1059,6 +1594,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 +1619,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 +1855,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 +1933,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 +2000,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();
@@ -1905,14 +2468,31 @@
             if (serviceSubtaskCountReq.getDrcode() != null && !serviceSubtaskCountReq.getDrcode().isEmpty()) {
                 groupKey = drcode;
             } else {
-                groupKey = "deptcode";
+                // 2026-11 瀛楁閲嶅懡鍚嶅悗锛歞eptcode 鏄湪闄㈢瀹わ紙鍘嗗彶鏁版嵁涓虹┖锛夛紝
+                // 鍑洪櫌绉戝宸叉敼鍚嶄负 leaveldeptcode锛屽垎缁勫繀椤荤敤鍚庤�咃紝
+                // 鍚﹀垯 getSfStatisticsGroupKey 浼氳繑鍥� NULL锛屾嫾鎴� IN (NULL) 瀵艰嚧鏁磋〃鏌ョ┖銆�
+                groupKey = "leaveldeptcode";
             }
+        } else if (serviceSubtaskCountReq.getStatisticaltype() != null && serviceSubtaskCountReq.getStatisticaltype() == 3) {
+            // 鐥呯缁熻锛氭寜浠诲姟ID鍒嗙粍锛屾瘡涓换鍔″嵆涓�涓梾绉嶉殢璁夸换鍔�
+            groupKey = "taskid";
         }
         List<ServiceSubtaskStatistic> serviceSubtaskStatistics = new ArrayList<>();
         // 鏍规嵁鏉′欢杩涜鍒嗙粍
         Map<String, List<ServiceSubtask>> collect = new HashMap<>();
         serviceSubtaskCountReq.setGroupKey(groupKey);
         List<String> groupKeyList = serviceSubtaskMapper.getSfStatisticsGroupKey(serviceSubtaskCountReq);
+        /*
+         * 鍒嗙粍閿彲鑳戒负 NULL锛堝巻鍙叉暟鎹�佹垨閲嶅懡鍚嶅悗灏氭湭鍥炲~鐨勫湪闄㈢淮搴﹀垪锛夛紝
+         * 鐩存帴鎷艰繘 SQL 浼氬彉鎴� `AND IFNULL(鍒�,'Unknown') IN (NULL)`锛屾亽涓嶆垚绔嬶紝鏁村紶缁熻琛ㄤ负绌恒��
+         * 杩欓噷缁熶竴鎴� 'Unknown'锛屼笌鑱氬悎 SQL 鐨� IFNULL(鍒�,'Unknown') 鍙e緞瀵归綈銆�
+         */
+        if (groupKeyList != null) {
+            groupKeyList = groupKeyList.stream()
+                    .map(key -> key == null ? "Unknown" : key)
+                    .distinct()
+                    .collect(Collectors.toList());
+        }
         serviceSubtaskCountReq.setGroupKeyList(groupKeyList);
         List<ServiceSubtask> rawData = serviceSubtaskMapper.getSfStatistics(serviceSubtaskCountReq);
         switch (groupKey) {
@@ -1927,10 +2507,14 @@
                 collect = rawData.stream().collect(Collectors.groupingBy(subtask -> Optional.ofNullable(subtask.getManagementDoctorCode()).orElse("Unknown")));
                 break;
             case "deptcode":
+            case "leaveldeptcode":
                 collect = rawData.stream().collect(Collectors.groupingBy(subtask -> Optional.ofNullable(subtask.getLeaveldeptcode()).orElse("Unknown")));
                 break;
             case "leavehospitaldistrictcode":
                 collect = rawData.stream().collect(Collectors.groupingBy(subtask -> Optional.ofNullable(subtask.getLeavehospitaldistrictcode()).orElse("Unknown")));
+                break;
+            case "taskid":
+                collect = rawData.stream().collect(Collectors.groupingBy(subtask -> Optional.ofNullable(subtask.getTaskid()).map(String::valueOf).orElse("Unknown")));
                 break;
             default:
                 collect = rawData.stream().collect(Collectors.groupingBy(subtask -> Optional.ofNullable(subtask.getDrcode()).orElse("Unknown")));
@@ -1944,6 +2528,9 @@
                 continue;
             }
             ServiceSubtaskStatistic statistic = getStatistic(serviceSubtaskList, serviceSubtaskCountReq.getFollowUpCountStyle(), serviceSubtaskCountReq.getVisitCount());
+            if ("taskid".equals(groupKey)) {
+                applyTaskGroupInfo(statistic, serviceSubtaskList);
+            }
             if (ObjectUtils.isNotEmpty(jsRates)) {
                 Map<String, Object> jsRateMap = jsRates.get(collectKey);
                 BigDecimal jsRate = new BigDecimal("0");
@@ -2032,7 +2619,16 @@
         ssRate.setGroupKey(groupKey);
         ssRate.setGroupKeyList(groupKeyList);
         ssRate.setRateDay(serviceSubtaskCountReq.getRateDay());
-        return serviceSubtaskMapper.selectTimelyRateBatch(ssRate);
+        Map<String, Map<String, Object>> rateResult = serviceSubtaskMapper.selectTimelyRateBatch(ssRate);
+        if (rateResult == null || rateResult.isEmpty()) {
+            return rateResult;
+        }
+        // taskid 涓烘暟鍊肩被鍨嬶紝MyBatis 杩斿洖鐨� Map key 鍙兘鏄� Long锛岃繖閲岀粺涓�杞垚 String 涓庡垎缁� key 瀵归綈
+        Map<String, Map<String, Object>> jsRates = new HashMap<>();
+        for (Map.Entry<String, Map<String, Object>> entry : rateResult.entrySet()) {
+            jsRates.put(String.valueOf(entry.getKey()), entry.getValue());
+        }
+        return jsRates;
     }
 
     /**
@@ -2056,6 +2652,9 @@
             ServiceSubtaskStatistic totalRow = calculateTotalRow(sfStatistics);
             totalRow.setLeavehospitaldistrictname("鎬昏");
             totalRow.setDeptname("鎬昏");
+            if (serviceSubtaskCountReq.getStatisticaltype() != null && serviceSubtaskCountReq.getStatisticaltype() == 3) {
+                totalRow.setTaskName("鎬昏");
+            }
             sfStatistics.add(totalRow);
         }
 
@@ -2154,6 +2753,22 @@
         return serviceSubtaskStatistics;
     }
 
+    /**
+     * 鐥呯缁熻锛堟寜 taskid 鍒嗙粍锛夋椂琛ラ綈浠诲姟ID銆佷换鍔″悕绉板拰鐤剧梾鐥呯銆�
+     *
+     * @param statistic          缁熻缁撴灉
+     * @param serviceSubtaskList 褰撳墠浠诲姟鍒嗙粍涓嬬殑瀛愪换鍔�
+     */
+    private void applyTaskGroupInfo(ServiceSubtaskStatistic statistic, List<ServiceSubtask> serviceSubtaskList) {
+        if (statistic == null || CollectionUtils.isEmpty(serviceSubtaskList)) {
+            return;
+        }
+        ServiceSubtask first = serviceSubtaskList.get(0);
+        statistic.setTaskId(first.getTaskid());
+        statistic.setTaskName(first.getTaskName());
+        statistic.setDiagType(first.getDiagType());
+    }
+
     private ServiceSubtaskStatistic getStatistic(List<ServiceSubtask> serviceSubtaskList, String followUpCountStyle, Integer visitCount) {
         ServiceSubtaskStatistic serviceSubtaskStatistic = new ServiceSubtaskStatistic();
 
@@ -2170,6 +2785,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 +2801,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 +2947,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 +3086,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=7锛夈��
+     */
+    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=7 缁熻
+        if (StringUtils.equals("7", StringUtils.trim(serviceSubtask.getSuggest()))) {
+            statistic.setSuggest7CountAgain(statistic.getSuggest7CountAgain() + 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 +3180,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 +3191,10 @@
             //搴旈殢璁夸汉娆�
             if (serviceSubtask.getSendstate() != null && serviceSubtask.getSendstate() != 4) {
                 serviceSubtaskStatistic.setFollowUpNeeded(serviceSubtaskStatistic.getFollowUpNeeded() + 1L);
+            }
+            //鍙婃椂闅忚浜烘锛氬凡瀹屾垚涓旀湭瓒呮椂
+            if (isTimelyFollowUp(serviceSubtask)) {
+                serviceSubtaskStatistic.setTimelyFollowUp(serviceSubtaskStatistic.getTimelyFollowUp() + 1L);
             }
 
             //棣栨鍑洪櫌闅忚
@@ -2635,6 +3354,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 +3365,10 @@
             //搴旈殢璁夸汉娆�
             if (serviceSubtask.getSendstate() != null && serviceSubtask.getSendstate() != 4) {
                 serviceSubtaskStatistic.setFollowUpNeeded(serviceSubtaskStatistic.getFollowUpNeeded() + 1L);
+            }
+            //鍙婃椂闅忚浜烘锛氬凡瀹屾垚涓旀湭瓒呮椂
+            if (isTimelyFollowUp(serviceSubtask)) {
+                serviceSubtaskStatistic.setTimelyFollowUp(serviceSubtaskStatistic.getTimelyFollowUp() + 1L);
             }
 
             //鍐嶆鍑洪櫌闅忚
@@ -2901,7 +3625,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 +3735,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 +3771,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
+     * suggest7CountAgainInfo - visitCount > 1 && suggest = 7
      * filterCountList - taskSituation = 6 && sendstate = 4
      *
      */
@@ -3050,7 +3790,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 +3814,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 +3883,7 @@
                 serviceSubtaskCountReq.setVisitCount(1);
                 serviceSubtaskCountReq.setCurrentPreachform("4");
                 break;
-            case "wechatInfo":
+            case "weChatInfo":
                 serviceSubtaskCountReq.setVisitCount(1);
                 serviceSubtaskCountReq.setCurrentPreachform("5");
                 break;
@@ -3134,7 +3902,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 +3939,7 @@
                 serviceSubtaskCountReq.setVisitCount(2);
                 serviceSubtaskCountReq.setCurrentPreachform("4");
                 break;
-            case "wechatAgainInfo":
+            case "weChatAgainInfo":
                 serviceSubtaskCountReq.setVisitCount(2);
                 serviceSubtaskCountReq.setCurrentPreachform("5");
                 break;
@@ -3190,7 +3958,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 +3966,11 @@
             case "abnormalAgainInfo":
                 serviceSubtaskCountReq.setVisitCount(2);
                 serviceSubtaskCountReq.setExcep("1");
+                break;
+            case "suggest7CountAgainInfo":
+                // 鍐嶆闅忚鍥為櫌澶嶈瘖澶嶆煡锛歷isitCount > 1 涓� suggest = 7
+                serviceSubtaskCountReq.setVisitCount(2);
+                serviceSubtaskCountReq.setSuggest("7");
                 break;
             case "taskSituation1Info":
                 serviceSubtaskCountReq.setTaskSituation(1);
@@ -3221,9 +3994,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 +4052,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<>();
@@ -3255,6 +4074,15 @@
         } 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";
+        } else if (serviceSubtaskCountReq.getStatisticaltype() != null && serviceSubtaskCountReq.getStatisticaltype() == 3) {
+            // 鐥呯缁熻锛氭寜浠诲姟ID鍒嗙粍锛屾瘡涓换鍔″嵆涓�涓梾绉嶉殢璁夸换鍔�
+            groupKey = "taskid";
         }
         serviceSubtaskCountReq.setGroupKey(groupKey);
         // 鍒嗙粍鍒椾笌杩囨护鍒椾繚鎸佷竴鑷达細绂婚櫌绉戝 leaveldeptcode銆佸湪闄㈢瀹� deptcode
@@ -3298,6 +4126,35 @@
         List<ServiceSubtaskStatistic> sumStatistics = serviceSubtaskMapper.getSfStatisticsScriptSum(serviceSubtaskCountReq);
         if (CollectionUtils.isEmpty(sumStatistics)) {
             return serviceSubtaskStatistics;
+        }
+
+        /*
+         * 鐥呯缁熻锛堟寜 taskid 鍒嗙粍锛夋椂琛ラ綈浠诲姟ID銆佷换鍔″悕绉板拰鐤剧梾鐥呯銆�
+         * 鑱氬悎 SQL 宸茶繑鍥� taskid/task_name锛岃繖閲屽啀琛� diag_type銆�
+         */
+        if ("taskid".equals(groupKey)) {
+            Map<Long, String> diagTypeCache = new HashMap<>();
+            for (ServiceSubtaskStatistic statistic : sumStatistics) {
+                Long taskId = statistic.getTaskId();
+                if (taskId == null && StringUtils.isNotEmpty(statistic.getGroupCode())) {
+                    try {
+                        taskId = Long.valueOf(statistic.getGroupCode());
+                    } catch (NumberFormatException ignore) {
+                        // 鍘嗗彶鏁版嵁鍙兘涓嶆槸鏁板�� taskid锛屼繚鎸佸師鍒嗙粍閿嵆鍙�
+                    }
+                }
+                statistic.setTaskId(taskId);
+                if (StringUtils.isEmpty(statistic.getTaskName())) {
+                    statistic.setTaskName(statistic.getGroupName());
+                }
+                if (taskId != null) {
+                    if (!diagTypeCache.containsKey(taskId)) {
+                        ServiceTask task = serviceTaskService.selectServiceTaskByTaskid(taskId);
+                        diagTypeCache.put(taskId, task == null ? null : task.getDiagType());
+                    }
+                    statistic.setDiagType(diagTypeCache.get(taskId));
+                }
+            }
         }
 
         /*
@@ -3441,8 +4298,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 +4322,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,32 +4369,125 @@
                 }
             }
             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;
+    }
+
+    /**
+     * 闂閫夐」鏁板瓧涓嬮捇锛氳繑鍥為�夋嫨浜嗘寚瀹氶�夐」鐨� service_subtask 鍒楄〃銆�
+     * <p>
+     * 缁熻鏄庣粏涓殑閫夐」鏁伴噺鏉ヨ嚜 service_subtask_detail 鐨�
+     * IFNULL(matchedtext, asrtext)锛屽洜姝や笅閽诲繀椤讳娇鐢ㄥ悓涓�棰樼洰鐨勭浉鍚岄�夐」杩涜鍖归厤銆�
+     *
+     * @param serviceSubtaskCountReq 涓嬮捇鏉′欢
+     * @return service_subtask 鍒楄〃
+     */
+    @Override
+    public List<ServiceSubtask> getSfStatisticsScriptDetailsHyperlink(ServiceSubtaskCountReq serviceSubtaskCountReq) {
+        log.info("getSfStatisticsScriptDetailsHyperlink鐨勫叆鍙備负锛歿}", serviceSubtaskCountReq);
+        if (serviceSubtaskCountReq == null) {
+            return new ArrayList<>();
+        }
+        // 鍏煎鍓嶇鐩存帴鍥炰紶缁熻鏄庣粏琛屼腑鐨� subtaskType
+        if (StringUtils.isBlank(serviceSubtaskCountReq.getType())
+                && StringUtils.isNotBlank(serviceSubtaskCountReq.getSubtaskType())) {
+            serviceSubtaskCountReq.setType(serviceSubtaskCountReq.getSubtaskType());
+        }
+        resolveScriptContentAndType(serviceSubtaskCountReq);
+        if (StringUtils.isBlank(serviceSubtaskCountReq.getScriptContent())
+                && serviceSubtaskCountReq.getScriptid() == null) {
+            log.warn("getSfStatisticsScriptDetailsHyperlink-棰樼洰scriptid鍜宻criptContent鍧囦负绌猴紝杩斿洖绌哄垪琛�");
+            return new ArrayList<>();
+        }
+        if (serviceSubtaskCountReq.getOptionText() == null) {
+            log.warn("getSfStatisticsScriptDetailsHyperlink-閫夐」optionText涓虹┖锛岃繑鍥炵┖鍒楄〃");
+            return new ArrayList<>();
+        }
+        return serviceSubtaskMapper.getSfStatisticsScriptDetailsHyperlink(serviceSubtaskCountReq);
+    }
+
+    /**
+     * 鏍规嵁 scriptid 琛ラ綈棰樼洰鍐呭鍜屼换鍔$被鍨嬨��
+     * <p>
+     * 缁熻鏄庣粏鎸夆�滈鐩唴瀹� + 浠诲姟绫诲瀷鈥濆悎骞讹紝鍚屼竴閬撻澶嶅埗鍒板涓换鍔℃ā鏉挎椂 scriptid 鍙兘涓嶅悓锛�
+     * 鍥犳浼樺厛鎸夐鐩唴瀹逛笅閽伙紱鍙湁鏌ヤ笉鍒伴鐩唴瀹规椂鎵嶉��鍖栦负 scriptid 绮剧‘鍖归厤銆�
+     */
+    private void resolveScriptContentAndType(ServiceSubtaskCountReq serviceSubtaskCountReq) {
+        if (serviceSubtaskCountReq.getScriptid() == null) {
+            return;
+        }
+        // 宸叉湁棰樼洰鍐呭鍜屼换鍔$被鍨嬫椂鏃犻渶鍐嶆煡妯℃澘闂琛�
+        if (StringUtils.isNotBlank(serviceSubtaskCountReq.getScriptContent())
+                && StringUtils.isNotBlank(serviceSubtaskCountReq.getType())) {
+            return;
+        }
+
+        String type = serviceSubtaskCountReq.getType();
+        if (StringUtils.isBlank(type) || "1".equals(type)) {
+            IvrTaskTemplateScript ivrTaskTemplateScript =
+                    iIvrTaskTemplateScriptService.selectIvrTaskTemplateScriptByID(serviceSubtaskCountReq.getScriptid());
+            if (ObjectUtils.isNotEmpty(ivrTaskTemplateScript)) {
+                if (StringUtils.isBlank(serviceSubtaskCountReq.getType())) {
+                    serviceSubtaskCountReq.setType("1");
+                }
+                if (StringUtils.isBlank(serviceSubtaskCountReq.getScriptContent())) {
+                    serviceSubtaskCountReq.setScriptContent(ivrTaskTemplateScript.getScriptContent());
+                }
+                return;
+            }
+            // 鏄庣‘鎸囧畾浜嗚闊崇被鍨嬫椂涓嶅啀灏濊瘯闂嵎琛�
+            if (StringUtils.isNotBlank(type)) {
+                return;
+            }
+        }
+
+        if (StringUtils.isBlank(serviceSubtaskCountReq.getType()) || "2".equals(type)) {
+            SvyTaskTemplateScript svyTaskTemplateScript =
+                    svyTaskTemplateScriptService.selectSvyTaskTemplateScriptByID(serviceSubtaskCountReq.getScriptid());
+            if (ObjectUtils.isNotEmpty(svyTaskTemplateScript)) {
+                if (StringUtils.isBlank(serviceSubtaskCountReq.getType())) {
+                    serviceSubtaskCountReq.setType("2");
+                }
+                if (StringUtils.isBlank(serviceSubtaskCountReq.getScriptContent())) {
+                    serviceSubtaskCountReq.setScriptContent(svyTaskTemplateScript.getScriptContent());
+                }
+            }
+        }
     }
 
     /**
@@ -3536,6 +4501,8 @@
 
         // 鍒濆鍖栨暟鍊煎瓧娈�
         total.setDischargeCount(0);
+        total.setFirstDischargeCount(0L);
+        total.setAgainFollowUpPushCount(0L);
         total.setNonFollowUp(0L);
         total.setFilterCount(0L);
         total.setFollowUpNeeded(0L);
@@ -3543,14 +4510,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.setSuggest7CountAgain(0L);
         total.setManualAgain(0L);
         total.setSmsAgain(0L);
         total.setWeChatAgain(0L);
@@ -3567,6 +4546,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 +4555,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.setSuggest7CountAgain(total.getSuggest7CountAgain() + (stat.getSuggest7CountAgain() != null ? stat.getSuggest7CountAgain() : 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));
@@ -3833,6 +4826,7 @@
             serviceSubtask.setSendstate(6L);
             serviceSubtask.setPreachform("1");
             serviceSubtaskMapper.updateServiceSubtask(serviceSubtask);
+            followupCompletedEventPublisher.publish(serviceSubtask.getId());
         }
     }
 
@@ -3904,6 +4898,185 @@
         }
     }
 
+    /**
+     * 寰俊鍏紬鍙峰凡棰嗗彇鏈~鍐欙細鎸夐鐩粯璁ら�夐」琛ラ綈褰撳墠瀛愪换鍔★紝骞跺皢褰撳墠瀛愪换鍔″畬鎴愩��
+     * <p>
+     * 鎵�鏈夋煡璇€�佹槑缁嗗啓鍏ャ�佺姸鎬佹洿鏂伴兘鍙娇鐢ㄥ綋鍓� subid锛岄伩鍏嶅奖鍝嶅叾浠栦换鍔�/瀛愪换鍔°��
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean completeWechatClaimedByDefault(ServiceSubtask serviceSubtask) {
+        if (serviceSubtask == null || serviceSubtask.getId() == null) {
+            return false;
+        }
+
+        // 閲嶆柊璇诲彇褰撳墠瀛愪换鍔★紝鎵�鏈夊垽鏂彧閽堝杩欎竴涓� subid
+        ServiceSubtask current = serviceSubtaskMapper.selectServiceSubtaskById(serviceSubtask.getId());
+        if (current == null) {
+            return false;
+        }
+        // 鍙鐞嗭細涓芥按涓尰闄€�侀棶鍗枫�佸綋鍓嶆柟寮忓叕浼楀彿銆佸凡棰嗗彇銆佹湭鎻愪氦
+        // 鍏朵粬鍖婚櫌 orgid 涓嶅尮閰嶆椂鐩存帴杩斿洖 false锛岀户缁蛋鍘熻ˉ鍋块�昏緫銆�
+        if (!IServiceSubtaskService.WECHAT_DEFAULT_COMPLETE_ORGID.equals(current.getOrgid())
+                || !"2".equals(current.getType())
+                || !"5".equals(current.getCurrentPreachform())
+                || !Long.valueOf(1L).equals(current.getSendstate())
+                || Long.valueOf(1L).equals(current.getSubmit())) {
+            return false;
+        }
+
+        // 鏌ヨ褰撳墠瀛愪换鍔¤嚜宸辩殑鍏紬鍙峰彂閫佹柟寮忥紝蹇呴』鏄凡鍙戦��(2)鎴栧凡棰嗗彇(3)
+        ServiceSubtaskPreachform query = new ServiceSubtaskPreachform();
+        query.setSubid(current.getId());
+        query.setTaskid(current.getTaskid());
+        query.setPreachform("5");
+        List<ServiceSubtaskPreachform> preachforms = taskPreachformPort.selectServiceSubtaskPreachformList(query);
+        if (CollectionUtils.isEmpty(preachforms)) {
+            return false;
+        }
+        ServiceSubtaskPreachform currentPreachform = preachforms.stream()
+                .filter(item -> "2".equals(item.getSendstate()) || "3".equals(item.getSendstate()))
+                .findFirst().orElse(null);
+        if (currentPreachform == null) {
+            return false;
+        }
+
+        // 鍙粰褰撳墠 subid 琛ラ綈榛樿閫夐」锛涜嚦灏戞湁涓�棰橀粯璁ら�夐」鎵嶇户缁�
+        int filled = fillWechatDefaultDetailForCurrentSubtask(current);
+        if (filled <= 0) {
+            log.info("銆愬井淇″叕浼楀彿榛樿瀹屾垚銆憇ubid锛歿} 娌℃湁鍙~鍏呯殑榛樿閫夐」锛岃烦杩�", current.getId());
+            return false;
+        }
+
+        // 褰撳墠鍏紬鍙峰彂閫佹柟寮忥細宸插彂閫�/宸查鍙� -> 瀹屾垚(9)
+        ServiceSubtaskPreachform updatePreachform = new ServiceSubtaskPreachform();
+        updatePreachform.setId(currentPreachform.getId());
+        updatePreachform.setSubid(current.getId());
+        updatePreachform.setPreachform("5");
+        updatePreachform.setOrgid(current.getOrgid());
+        updatePreachform.setRemark("宸查鍙栨湭濉啓锛屾寜榛樿閫夐」瀹屾垚");
+        int preachformUpdated = taskPreachformPort.updateWechatPreachformComplete(updatePreachform);
+        if (preachformUpdated == 0) {
+            throw new BaseException("寰俊鍏紬鍙烽粯璁ゅ畬鎴愬け璐ワ細鍙戦�佹柟寮忕姸鎬佸凡鍙樺寲锛宻ubid=" + current.getId());
+        }
+
+        // 褰撳墠瀛愪换鍔★細宸查鍙�(1) -> 瀹屾垚(6)锛屽彧鏇存柊杩欎竴涓� subid
+        ServiceSubtask complete = new ServiceSubtask();
+        complete.setId(current.getId());
+        complete.setOrgid(current.getOrgid());
+        complete.setFinishtime(new Date());
+        complete.setResult("success");
+        complete.setRemark("宸查鍙栨湭濉啓锛屾寜榛樿閫夐」瀹屾垚");
+        int subtaskUpdated = serviceSubtaskMapper.updateWechatDefaultComplete(complete);
+        if (subtaskUpdated == 0) {
+            throw new BaseException("寰俊鍏紬鍙烽粯璁ゅ畬鎴愬け璐ワ細瀛愪换鍔$姸鎬佸凡鍙樺寲锛宻ubid=" + current.getId());
+        }
+        followupCompletedEventPublisher.publish(current.getId());
+
+        log.info("銆愬井淇″叕浼楀彿榛樿瀹屾垚銆憇ubid锛歿}锛屽凡鎸夐粯璁ら�夐」瀹屾垚锛屽~鍏呴鐩暟锛歿}", current.getId(), filled);
+        return true;
+    }
+
+    /**
+     * 浠呯粰褰撳墠瀛愪换鍔¤ˉ榻愰棶鍗峰崟閫�/澶氶�夐涓� default_value=2 鐨勯�夐」銆�
+     * 宸叉湁闈炵┖绛旀鐨勯鐩烦杩囷紝涓嶈鐩栨偅鑰呭凡濉啓鍐呭銆�
+     *
+     * @return 瀹為檯濉厖锛堟垨姝ゅ墠宸茬敱绯荤粺榛樿濉厖锛夌殑棰樼洰鏁�
+     */
+    private int fillWechatDefaultDetailForCurrentSubtask(ServiceSubtask serviceSubtask) {
+        Long templateId = serviceSubtask.getTemplateid();
+        ServiceTask serviceTask = serviceTaskService.selectServiceTaskByTaskid(serviceSubtask.getTaskid());
+        if (templateId == null && serviceTask != null) {
+            templateId = serviceTask.getTemplateid();
+        }
+        if (templateId == null) {
+            return 0;
+        }
+
+        SvyTaskTemplateScript scriptQuery = new SvyTaskTemplateScript();
+        scriptQuery.setTemplateID(templateId);
+        List<SvyTaskTemplateScript> scripts = svyTaskTemplateScriptService.selectSvyTaskTemplateScriptList(scriptQuery);
+        if (CollectionUtils.isEmpty(scripts)) {
+            return 0;
+        }
+
+        int filled = 0;
+        for (SvyTaskTemplateScript script : scripts) {
+            SvyTaskTemplateScriptVO scriptVO = DtoConversionUtils.sourceToTarget(script, SvyTaskTemplateScriptVO.class);
+            SvyTaskTemplateScriptVO scriptWithOptions = svyTaskTemplateScriptService.selectInfoByCondition(scriptVO);
+            if (scriptWithOptions == null
+                    || scriptWithOptions.getId() == null
+                    || CollectionUtils.isEmpty(scriptWithOptions.getSvyTaskTemplateTargetoptions())
+                    || (!"1".equals(scriptWithOptions.getScriptType()) && !"2".equals(scriptWithOptions.getScriptType()))) {
+                continue;
+            }
+
+            List<SvyTaskTemplateTargetoption> options = scriptWithOptions.getSvyTaskTemplateTargetoptions();
+            List<SvyTaskTemplateTargetoption> defaultOptions = options.stream()
+                    .filter(option -> option.getDefaultValue() != null && option.getDefaultValue() == 2L)
+                    .filter(option -> StringUtils.isNotBlank(option.getOptioncontent()))
+                    .collect(Collectors.toList());
+            if (CollectionUtils.isEmpty(defaultOptions)) {
+                continue;
+            }
+
+            // 宸叉湁闈炵┖绛旀鍒欎笉瑕嗙洊锛岄伩鍏嶅奖鍝嶆偅鑰呭凡濉啓鍐呭
+            ServiceSubtaskDetail detailQuery = new ServiceSubtaskDetail();
+            detailQuery.setSubId(serviceSubtask.getId());
+            detailQuery.setScriptid(scriptWithOptions.getId());
+            List<ServiceSubtaskDetail> existingDetails = serviceSubtaskDetailMapper.selectServiceSubtaskDetailList(detailQuery);
+            ServiceSubtaskDetail existing = CollectionUtils.isNotEmpty(existingDetails) ? existingDetails.get(0) : null;
+            if (existing != null
+                    && (StringUtils.isNotBlank(existing.getAsrtext()) || StringUtils.isNotBlank(existing.getMatchedtext()))) {
+                // 涔嬪墠宸茬敱绯荤粺榛樿濉厖杩囩殑锛岃鍏ュ凡濉厖锛屼繚璇侀噸璇曞箓绛�
+                if ("DEFAULT".equals(existing.getOperate())) {
+                    filled++;
+                }
+                continue;
+            }
+
+            String targetValue = options.stream()
+                    .map(SvyTaskTemplateTargetoption::getOptioncontent)
+                    .filter(StringUtils::isNotBlank)
+                    .collect(Collectors.joining("&"));
+            String defaultValue = defaultOptions.stream()
+                    .map(SvyTaskTemplateTargetoption::getOptioncontent)
+                    .collect(Collectors.joining("&"));
+
+            ServiceSubtaskDetail detail = new ServiceSubtaskDetail();
+            detail.setSubId(serviceSubtask.getId());
+            detail.setTaskid(serviceSubtask.getTaskid());
+            detail.setPatid(serviceSubtask.getPatid());
+            detail.setScriptid(scriptWithOptions.getId());
+            detail.setTemplatequestionnum(scriptWithOptions.getId());
+            detail.setQuestiontext(scriptWithOptions.getScriptContent());
+            detail.setCategoryid(scriptWithOptions.getCategoryid());
+            detail.setTargetid(scriptWithOptions.getTargetid() != null ? scriptWithOptions.getTargetid().intValue() : null);
+            detail.setValueType(scriptWithOptions.getScriptType());
+            detail.setTargetvalue(targetValue);
+            detail.setAsrtext(defaultValue);
+            detail.setMatchedtext(defaultValue);
+            detail.setOperate("DEFAULT");
+            detail.setTemplateType(2);
+            detail.setLibTemplateid(serviceSubtask.getLibtemplateid() != null ? serviceSubtask.getLibtemplateid().intValue() : null);
+            detail.setTemplateid(templateId.toString());
+            detail.setGuid(scriptWithOptions.getGuid());
+            detail.setOrgid(StringUtils.isNotEmpty(scriptWithOptions.getOrgid()) ? scriptWithOptions.getOrgid()
+                    : (serviceTask != null ? serviceTask.getOrgid() : null));
+            detail.setCreateTime(new Date());
+            detail.setUpdateTime(new Date());
+
+            if (existing != null) {
+                detail.setId(existing.getId());
+                serviceSubtaskDetailMapper.updateServiceSubtaskDetail(detail);
+            } else {
+                serviceSubtaskDetailMapper.insertServiceSubtaskDetail(detail);
+            }
+            filled++;
+        }
+        return filled;
+    }
+
     @Override
     public Map<String, Object> getHeLibraryCount(@RequestBody HeLibraryCountVO heLibraryCountVO) {
         if (ObjectUtils.isEmpty(heLibraryCountVO)) {
@@ -3972,12 +5145,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);
@@ -3987,7 +5154,7 @@
         serviceOutPath.setCreateTime(new Date());
         serviceOutPath.setOrgid(serviceSubtask.getOrgid());
         iServiceOutPathService.insertServiceOutPath(serviceOutPath);
-        String format = String.format("%03X", serviceOutPath.getId());
+        String format = ShortCode.encode(serviceOutPath.getId());
         String url = "";
         SendMagParam sendMagParam = new SendMagParam();
         sendMagParam.setType("4");
@@ -4025,6 +5192,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<>();
@@ -4052,14 +5238,22 @@
             serviceOutPath.setCreateTime(new Date());
             serviceOutPath.setOrgid(serviceSubtask.getOrgid());
             iServiceOutPathService.insertServiceOutPath(serviceOutPath);
-            String format = String.format("%03X", serviceOutPath.getId());
+            String format = ShortCode.encode(serviceOutPath.getId());
             serviceOutPath.setRadix(format);
             serviceOutPath.setUpdateTime(new Date());
             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;
@@ -4259,7 +5453,7 @@
                         serviceOutPath.setCreateTime(new Date());
                         serviceOutPath.setOrgid(serviceTask.getOrgid());
                         iServiceOutPathService.insertServiceOutPath(serviceOutPath);
-                        String format = String.format("%03X", serviceOutPath.getId());
+                        String format = ShortCode.encode(serviceOutPath.getId());
                         serviceOutPath.setRadix(format);
                         serviceOutPath.setUpdateTime(new Date());
                         String url = StringUtils.isNotEmpty(req_path) ? ip + ":" + req_path + "/wt?p=" + format : ip + "/wt?p=" + format;

--
Gitblit v1.9.3