From d7e3192804fca756d881af360ffd5eea157ef9ad Mon Sep 17 00:00:00 2001
From: 陈昶聿 <chychen@nbjetron.com>
Date: 星期三, 21 十月 2026 15:45:18 +0800
Subject: [PATCH] 【市一】电话挂断服务

---
 smartor/src/main/java/com/smartor/service/impl/ServiceSubtaskServiceImpl.java |  504 ++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 456 insertions(+), 48 deletions(-)

diff --git a/smartor/src/main/java/com/smartor/service/impl/ServiceSubtaskServiceImpl.java b/smartor/src/main/java/com/smartor/service/impl/ServiceSubtaskServiceImpl.java
index 2660766..c7f170a 100644
--- a/smartor/src/main/java/com/smartor/service/impl/ServiceSubtaskServiceImpl.java
+++ b/smartor/src/main/java/com/smartor/service/impl/ServiceSubtaskServiceImpl.java
@@ -18,7 +18,9 @@
 import com.ruoyi.common.utils.spring.SpringUtils;
 import com.ruoyi.system.domain.SysConfig;
 import com.ruoyi.system.mapper.SysConfigMapper;
+import com.ruoyi.system.mapper.SysDeptMapper;
 import com.ruoyi.system.service.ISysConfigService;
+import com.ruoyi.system.service.ISysDeptService;
 import com.smartor.common.FtpService;
 import com.smartor.common.MtSubmitSmUtil;
 import com.smartor.common.ScheduleSubtaskBuilder;
@@ -58,6 +60,8 @@
 import java.time.format.DateTimeFormatter;
 import java.time.temporal.ChronoUnit;
 import java.util.*;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -137,6 +141,9 @@
     private IPatArchiveService patArchiveService;
 
     @Autowired
+    private ISysDeptService sysDeptService;
+
+    @Autowired
     private ScheduleSubtaskBuilder scheduleSubtaskBuilder;
 
     @Autowired
@@ -181,6 +188,30 @@
     @Value("${hangup}")
     private String hangup;
 
+    /**
+     * 鎸傛柇璇锋眰鏈�澶ч噸璇曟鏁帮紙鍚娆★級
+     */
+    private static final int HANGUP_MAX_RETRY = 3;
+
+    /**
+     * 鎸傛柇璇锋眰閲嶈瘯闂撮殧锛堟绉掞級
+     */
+    private static final long HANGUP_RETRY_INTERVAL_MS = 300L;
+
+    /**
+     * 鍏滃簳鎸傛柇鐨勬渶灏忓欢鏃讹紙姣锛夛細绛夊钩鍙版妸缁撴潫璇挱瀹屽啀鎸�
+     */
+    private static final long HANGUP_FALLBACK_MIN_DELAY_MS = 8000L;
+
+    /**
+     * 鍏滃簳鎸傛柇瀹氭椂鍣ㄣ��
+     * <p>
+     * 闆ㄧ划杩欏鐨勬寕鏂師鏈緷璧�"骞冲彴鎾畬缁撴潫璇悗鍐嶅洖璋冧竴娆� PlayEventCallback(playstop=true)"锛�
+     * 骞冲彴涓嶅洖璋冪數璇濆氨涓�鐩存寕鐫�銆傝繖閲岀敤涓�涓畧鎶ょ嚎绋嬫睜鍦ㄧ粨鏉熻涓嬪彂鍚庢寜瀛楁暟浼扮畻鏃堕暱鍋氬厹搴曟寕鏂紝
+     * 涓庡钩鍙板洖璋冭蛋鍚屼竴涓箓绛夋爣璁帮紝涓嶄細閲嶅鎸傛柇銆�
+     */
+    private ScheduledExecutorService hangupFallbackExecutor;
+
     @Value("${app_key_yq}")
     private String app_key_yq;
 
@@ -216,8 +247,14 @@
     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;
     @Autowired
     private PatMedOperationItemMapper patMedOperationItemMapper;
+    @Autowired
+    private SysDeptMapper sysDeptMapper;
 
     /**
      * 鏌ヨ鍗曚竴浠诲姟锛堥殢璁匡級
@@ -558,11 +595,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;
     }
@@ -633,6 +681,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);
         }
@@ -669,6 +718,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);
@@ -769,6 +819,7 @@
      * @return 缁撴灉
      */
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public String addSubTaskAgain(ServiceSubtask serviceSubtask) {
         ServiceSubtask serviceSubtask1 = selectServiceSubtaskById(serviceSubtask.getId());
         PatMedInhosp patMedInhosp = new PatMedInhosp();
@@ -778,7 +829,7 @@
         PatArchive patArchive = patArchiveMapper.selectPatArchiveByPatid(serviceSubtask1.getPatid());
         //鏅畞 鏂板鍐嶆闅忚涓嶈兘瑙﹀彂鎮h�呭啀鍏ラ櫌
         boolean inhospAgain = true;
-        if (serviceSubtask1.getOrgid().equals("47255004333112711A1001")) {
+        if ("47255004333112711A1001".equals(serviceSubtask1.getOrgid())) {
             inhospAgain = false;
         }
         if (patMedInhospList.size() > 0 && inhospAgain) {
@@ -792,13 +843,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 serviceSubtask2 = DtoConversionUtils.sourceToTarget(serviceSubtask, ServiceSubtask.class);
@@ -819,6 +874,155 @@
             return "鍐嶆闅忚浠诲姟鍒涘缓鎴愬姛";
         }
         return "鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屽啀璇锋閲嶈瘯";
+    }
+
+    public String addSubTaskAgainNextTaskId(ServiceSubtask serviceSubtask) {
+        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() + ", 鍐嶈娆¢噸璇�";
+            }
+            // 骞傜瓑鏍¢獙锛氬悓涓�鍘熶换鍔″彧鍏佽鐢熸垚涓�鏉′簩娆¢殢璁�
+            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 = patMedInhospService.selectPatMedInhospList(patMedInhospVo);
+            PatMedInhosp patMedInhosp = new PatMedInhosp();
+            if (CollectionUtils.isNotEmpty(patMedInhospList)) {
+                patMedInhosp = patMedInhospList.get(0);
+            } else {
+                log.error("鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屼换鍔$己灏� 瀵瑰簲鐨勫嚭闄㈣褰�, taskid = {}, 鍐嶈娆¢噸璇�", serviceSubtask.getTaskid());
+                return "鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屼换鍔$己灏� 瀵瑰簲鐨勫嚭闄㈣褰�, taskid = " + serviceSubtask.getTaskid() + ", 鍐嶈娆¢噸璇�";
+            }
+            if (ObjectUtils.isEmpty(serviceTask.getNexttaskid())) {
+                log.error("鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屼换鍔$己灏� nexttaskid, taskid = {}, 鍐嶈娆¢噸璇�", serviceSubtask.getTaskid());
+                return "鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屼换鍔$己灏� nexttaskid, taskid = " + serviceSubtask.getTaskid() + ", 鍐嶈娆¢噸璇�";
+            }
+            ServiceTask nextServiceTask = serviceTaskService.selectServiceTaskByTaskid(serviceTask.getNexttaskid());
+            if (ObjectUtils.isEmpty(nextServiceTask)) {
+                log.error("鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屾壘涓嶅埌瀵瑰簲浠诲姟 nexttaskid = {}, 鍐嶈娆¢噸璇�", serviceTask.getNexttaskid());
+                return "鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屾壘涓嶅埌瀵瑰簲浠诲姟 nexttaskid = " + serviceTask.getNexttaskid() + ", 鍐嶈娆¢噸璇�";
+            }
+            PatArchive patArchive = patArchiveService.selectPatArchiveByPatid(serviceSubtask.getPatid());
+            if (ObjectUtils.isEmpty(patArchive)) {
+                log.error("鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屾湭鎵惧埌鎮h�呮。妗� patid = {}, subTaskId = {}", serviceSubtask.getPatid(), serviceSubtask.getId());
+                return "鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屾湭鎵惧埌鎮h�呮。妗�, subTaskId = " + serviceSubtask.getId() + ", 鍐嶈娆¢噸璇�";
+            }
+            String config = configService.selectConfigByKey("visit.early.day");
+            Integer type = 1;
+            // 闅忚涓績绉戝
+            String centerDept = configService.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 + ", 鍐嶈娆¢噸璇�";
+            }
+            // 鎸変笅涓�浠诲姟鐢熸垚浜屾闅忚瀛愪换鍔★紙琛ㄥ崟銆佸彂閫佹柟寮忓彇鑷笅涓�浠诲姟锛�
+            ServiceSubtask serviceSubtaskAgain = patMedInhospService.boxedServiceSubtask(nextServiceTask,
+                    patMedInhosp, patArchive, config, type);
+            if (ObjectUtils.isEmpty(serviceSubtaskAgain) || ObjectUtils.isEmpty(serviceSubtaskAgain.getTaskid())) {
+                log.error("鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屼笅涓�浠诲姟鐢熸垚瀛愪换鍔′负绌�, subTaskId = {}, nextTaskId = {}", serviceSubtask.getId(), serviceTask.getNexttaskid());
+                return "鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屾偅鑰呭嚭闄㈣褰曚笉瀹屾暣鎴栦笅涓�浠诲姟閰嶇疆寮傚父, 鍐嶈娆¢噸璇�";
+            }
+            Date now = new Date();
+            serviceSubtaskAgain.setId(null);
+            serviceSubtaskAgain.setUpid(serviceSubtask.getId().intValue());
+            serviceSubtaskAgain.setVisitCount(ObjectUtils.isNotEmpty(serviceSubtask.getVisitCount())
+                    ? serviceSubtask.getVisitCount() + 1 : 1);
+            serviceSubtaskAgain.setSendstate(2L);
+            serviceSubtaskAgain.setIsVisitAgain(1);
+            serviceSubtaskAgain.setCreateTime(now);
+            serviceSubtaskAgain.setUpdateTime(now);
+
+//            // 浜屾闅忚鏃堕棿锛氫笂娆¢殢璁垮畬鎴愭椂闂达紙鏈畬鎴愬垯褰撳墠鏃堕棿锛�+ 涓嬩竴浠诲姟鍙戦�佸ぉ鏁帮紱杩囧幓鏃堕棿鍏滃簳涓哄綋鍓嶆椂闂�
+//            Date baseTime = ObjectUtils.isNotEmpty(serviceSubtask.getFinishtime()) ? serviceSubtask.getFinishtime() : now;
+//            int intervalDays = ObjectUtils.isNotEmpty(nextServiceTask.getSendDay())
+//                    ? nextServiceTask.getSendDay().intValue() : 0;
+//            Date nextVisitTime = DateUtils.addDays(baseTime, intervalDays);
+//            if (nextVisitTime.before(now)) {
+//                nextVisitTime = now;
+//            }
+//            serviceSubtaskAgain.setVisitTime(nextVisitTime);
+//            serviceSubtaskAgain.setLongSendTime(nextVisitTime);
+            serviceSubtaskAgain.setVisitType(serviceSubtask.getVisitType());
+            serviceSubtaskAgain.setVisitDeptCode(sysDept.getDeptCode());
+            serviceSubtaskAgain.setVisitDeptName(sysDept.getDeptName());
+            int i = serviceSubtaskMapper.insertServiceSubtask(serviceSubtaskAgain);
+            if (i != 1) {
+                return "鍐嶆闅忚浠诲姟鍒涘缓澶辫触锛屽啀璇锋閲嶈瘯";
+            }
+            // 鍒濆鍖栧彂閫佹柟寮忔槑缁嗭紝琛ュ伩锛坈ompensateTask锛夊拰瓒呮椂鍒ゆ柇渚濊禆璇ヨ〃
+            patMedInhospService.addServiceSubtaskPreachform(serviceSubtaskAgain, null);
+            // 鏉′欢鏇存柊鍘熶换鍔★細0 琛岃鏄庡凡琚叾浠栬姹傜敓鎴愶紝鎶涘紓甯稿洖婊氭湰娆℃彃鍏ョ殑閲嶅鏁版嵁
+            int updated = serviceSubtaskMapper.updateIsVisitAgainById(serviceSubtask.getId(), 2);
+            if (updated != 1) {
+                throw new BaseException("鏃犻渶鍐嶆闅忚锛岃浠诲姟宸茬粡鍐嶆闅忚杩囦簡");
+            }
+            return "鍐嶆闅忚浠诲姟鍒涘缓鎴愬姛";
+        } finally {
+            unlockAgainTask(serviceSubtask.getId(), lockToken);
+        }
+    }
+
+    /**
+     * 鑾峰彇浜屾闅忚鐢熸垚閿併�俁edis 寮傚父鏃堕檷绾т负鏁版嵁搴撳箓绛夋牎楠岋紝涓嶉樆鏂笟鍔°��
+     */
+    private String tryLockAgainTask(Long subId) {
+        String token = UUID.randomUUID().toString();
+        try {
+            Boolean locked = redisTemplate.opsForValue().setIfAbsent(
+                    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 = redisTemplate.opsForValue().get(lockKey);
+            if (token.equals(current)) {
+                redisTemplate.delete(lockKey);
+            }
+        } catch (Exception e) {
+            log.warn("鍐嶆闅忚閲婃斁Redis閿佸け璐�, subId={}", subId, e);
+        }
     }
 
     /**
@@ -1818,6 +2022,130 @@
     }
 
     /**
+     * 缁撴潫璇紦瀛� key锛堝彧瑕佸啓杩囪 key 灏辫鏄庢湰娆″璇濆凡缁忔敹灏撅紝缁撴潫璇负绌轰篃绠楋級
+     */
+    private String jsyKey(String uuid) {
+        return uuid + "- jsy";
+    }
+
+    /**
+     * 鏈瀵硅瘽鏄惁宸茬粡鏀跺熬锛堝嵆宸茬粡涓嬪彂杩囩粨鏉熻锛夈��
+     * <p>
+     * 娉ㄦ剰锛氫笉鑳界敤 getCacheObject(...) != null 鍒ゆ柇锛岀粨鏉熻涓虹┖鏃跺啓鍏ョ殑鏄┖鍊硷紝
+     * 璇诲嚭鏉ユ槸 null 浣� key 鏄瓨鍦ㄧ殑锛屽繀椤荤敤 hasKey銆�
+     */
+    private boolean isDialogueEnded(String uuid) {
+        return StringUtils.isNotEmpty(uuid) && Boolean.TRUE.equals(redisCache.hasKey(jsyKey(uuid)));
+    }
+
+    /**
+     * 鎸傛柇鐢佃瘽锛氬箓绛� + 閲嶈瘯 + 寮傚父涓嶅鎶� + 娓呯悊缁撴潫璇紦瀛樸��
+     * <p>
+     * 骞冲彴鍥炶皟锛圥layEventCallback 鎾斁缁撴潫锛夊拰鍏滃簳瀹氭椂浠诲姟閮戒細璋冭繖閲岋紝
+     * 閫氳繃 redis 鐨� {uuid}- hangup-sent 鏍囪淇濊瘉鍚屼竴娆¢�氳瘽鍙湡姝f寕鏂竴娆°��
+     *
+     * @param uuid   閫氳瘽 uuid
+     * @param phone  琚彨鍙风爜
+     * @param reason 鎸傛柇鍘熷洜锛堜粎鐢ㄤ簬鏃ュ織鎺掓煡锛�
+     * @return true=鏈纭疄鍙戝嚭浜嗘寕鏂姹�
+     */
+    private boolean doHangup(String uuid, String phone, String reason) {
+        if (StringUtils.isEmpty(uuid)) {
+            log.warn("鎸傛柇璺宠繃锛歶uid 涓虹┖, reason={}", reason);
+            return false;
+        }
+        String sentKey = uuid + "- hangup-sent";
+        if (!redisCache.setCacheObjectIfAbsent(sentKey, "1", 30, TimeUnit.MINUTES)) {
+            log.info("鎸傛柇璺宠繃锛氳閫氳瘽宸叉寕鏂繃, uuid={}, reason={}", uuid, reason);
+            return false;
+        }
+        Map<String, String> req = new HashMap<>();
+        req.put("uuid", uuid);
+        req.put("caller", phone);
+        String body = new Gson().toJson(req);
+        for (int i = 1; i <= HANGUP_MAX_RETRY; i++) {
+            try {
+                // 鍙牎楠� HTTP 鐘舵�佺爜锛�/hangup 閫氬父鎴愬姛鍙洖 200 涓旀病鏈夊搷搴斾綋锛�
+                // 鐢� postJsonRequest 浼氬洜"鍝嶅簲涓簄ull"鎶婃垚鍔熻鍒ゆ垚澶辫触
+                HttpUtil.postJsonRequestAllowEmptyResponse(hangup, body);
+                log.info("鎸傛柇鎴愬姛, uuid={}, reason={}, 绗瑊}娆�, url={}", uuid, reason, i, hangup);
+                redisCache.setCacheObject(uuid + "- hangup", "1", 10, TimeUnit.MINUTES);
+                redisCache.deleteObject(jsyKey(uuid));
+                redisCache.deleteObject(uuid + "- hangup-planned");
+                return true;
+            } catch (Exception e) {
+                //鎶婂紓甯哥被鍨嬪拰淇℃伅鏀惧湪鍚屼竴琛岋紝鍗曡 grep 灏辫兘鐪嬪嚭鏄�"杩炰笉涓�"杩樻槸"杩斿洖闈�200"
+                log.error("鎸傛柇澶辫触, uuid={}, reason={}, 绗瑊}娆�, url={}, body={}, 鍘熷洜={}: {}",
+                        uuid, reason, i, hangup, body, e.getClass().getSimpleName(), e.getMessage(), e);
+                if (i < HANGUP_MAX_RETRY) {
+                    try {
+                        Thread.sleep(HANGUP_RETRY_INTERVAL_MS);
+                    } catch (InterruptedException ie) {
+                        Thread.currentThread().interrupt();
+                        break;
+                    }
+                }
+            }
+        }
+        // 鍏ㄩ儴澶辫触锛氶噴鏀惧箓绛夋爣璁帮紝鍏佽骞冲彴涓嬩竴娆″洖璋冩垨涓嬩竴涓厹搴曚换鍔¢噸璇�
+        redisCache.deleteObject(sentKey);
+        log.error("鎸傛柇鏈�缁堝け璐ワ紝宸查噴鏀鹃噸璇曟爣璁�, uuid={}, reason={}, url={}", uuid, reason, hangup);
+        return false;
+    }
+
+    /**
+     * 瀹夋帓涓�娆″厹搴曟寕鏂��
+     * <p>
+     * 缁撴潫璇笅鍙戝悗锛屽钩鍙版甯稿簲璇ュ啀鍥炶皟 PlayEventCallback(playstop=true)锛屾鏃剁敱 doHangup 绔嬪嵆鎸傛柇锛�
+     * 浣嗗钩鍙板瓧娈电己澶�/涓嶅洖璋冩椂鐢佃瘽浼氫竴鐩存寕鐫�锛屾墍浠ヨ繖閲屾寜缁撴潫璇瓧鏁颁及绠楁挱鏀炬椂闀匡紝鍒扮偣鍏滃簳鎸備竴娆°��
+     * 鍚屼竴涓� uuid 鍙畨鎺掍竴娆★紙{uuid}- hangup-planned 骞傜瓑锛夈��
+     *
+     * @param uuid 閫氳瘽 uuid
+     * @param phone 琚彨鍙风爜
+     * @param jsy   缁撴潫璇紙鍙兘涓虹┖锛屼负绌烘椂鎸夋渶灏忓欢鏃跺鐞嗭級
+     */
+    private void scheduleHangupFallback(String uuid, String phone, String jsy) {
+        if (StringUtils.isEmpty(uuid)) {
+            return;
+        }
+        if (!redisCache.setCacheObjectIfAbsent(uuid + "- hangup-planned", "1", 30, TimeUnit.MINUTES)) {
+            return;
+        }
+        // 涓枃 TTS 澶х害姣忕 3~4 涓瓧锛岀暀瓒充綑閲忓悗鍐嶅姞涓婃渶灏忓欢鏃�
+        int len = StringUtils.isEmpty(jsy) ? 0 : jsy.length();
+        long delay = HANGUP_FALLBACK_MIN_DELAY_MS + len * 400L;
+        if (delay > 60000L) {
+            delay = 60000L;
+        }
+        log.info("瀹夋帓鍏滃簳鎸傛柇, uuid={}, 缁撴潫璇暱搴�={}, 寤舵椂={}ms", uuid, len, delay);
+        getHangupFallbackExecutor().schedule(() -> {
+            try {
+                doHangup(uuid, phone, "缁撴潫璇挱瀹屽厹搴�");
+            } catch (Exception e) {
+                log.error("鍏滃簳鎸傛柇寮傚父, uuid={}", uuid, e);
+            }
+        }, delay, TimeUnit.MILLISECONDS);
+    }
+
+    /**
+     * 鍏滃簳鎸傛柇绾跨▼姹狅紙鎳掑姞杞斤紝瀹堟姢绾跨▼锛岃繘绋嬮��鍑轰笉鍙楀奖鍝嶏級
+     */
+    private ScheduledExecutorService getHangupFallbackExecutor() {
+        if (hangupFallbackExecutor == null) {
+            synchronized (this) {
+                if (hangupFallbackExecutor == null) {
+                    hangupFallbackExecutor = Executors.newScheduledThreadPool(2, r -> {
+                        Thread t = new Thread(r, "voice-hangup-fallback");
+                        t.setDaemon(true);
+                        return t;
+                    });
+                }
+            }
+        }
+        return hangupFallbackExecutor;
+    }
+
+    /**
      * 鐢佃瘽ASR閫氳瘽鍥炶皟(闆ㄧ划)
      *
      * @param phoneCallReqYQVO
@@ -1827,6 +2155,15 @@
         //鍏堟妸璇ubId浠巆ache_exist涓墧闄わ紝鍙鏈夊洖璋冨氨鍓旈櫎锛堢洰鐨勬槸锛屽敖蹇cache-exist涓虹┖锛屽幓缂撳瓨涓嬩竴鎵硅鎵撶殑鐢佃瘽;杩欓噷鍓旈櫎涓嶅奖鍝嶇數璇濈殑姝e父涓氬姟锛�
 //        redisCache.removeElementFromList("cache-exist", phoneCallReqYQVO.getTaskid());
 
+        //uuid / taskid / phone 鏄悗闈㈡墍鏈夐�昏緫鐨勫墠鎻愶紝缂轰簡灏辩洿鎺ュ拷鐣ワ紝閬垮厤 Long.valueOf(null)銆乶ull.trim() 涔嬬被鐨� 500
+        if (StringUtils.isEmpty(phoneCallReqYQVO.getUuid()) || StringUtils.isEmpty(phoneCallReqYQVO.getTaskid())
+                || StringUtils.isEmpty(phoneCallReqYQVO.getPhone())) {
+            log.warn("闆ㄧ划鍥炶皟缂哄皯 uuid/taskid/phone锛岀洿鎺ュ拷鐣�: operate={}, uuid={}, taskid={}, phone={}",
+                    phoneCallReqYQVO.getOperate(), phoneCallReqYQVO.getUuid(),
+                    phoneCallReqYQVO.getTaskid(), phoneCallReqYQVO.getPhone());
+            return new PhoneCallBackYQVO();
+        }
+
         //鍒ゆ柇鏄惁鎸傛満
         String hangupState = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "- hangup");
         Object cacheObject1 = redisCache.getCacheObject(phoneCallReqYQVO.getTaskid() + "recordAccept-hungup");
@@ -1835,7 +2172,7 @@
         }
         if ("AsrCallback".equals(phoneCallReqYQVO.getOperate()) && phoneCallReqYQVO.getEnd_time() != null && phoneCallReqYQVO.getEnd_time() != -1) {
             //澶勭悊鎸傛柇
-            if (phoneCallReqYQVO.getAsrtext().contains("鎷ㄥ彨鐨勭敤鎴锋蹇�")) {
+            if (StringUtils.isNotEmpty(phoneCallReqYQVO.getAsrtext()) && phoneCallReqYQVO.getAsrtext().contains("鎷ㄥ彨鐨勭敤鎴锋蹇�")) {
                 //鍒ゆ柇 recordAccept-hungup 鏄惁鏈夊�硷紝濡傛灉鏈夛紝鍒欒鏄庝笉鐢ㄥ鐞嗕簡锛屼箣鍓嶅凡缁忓鐞嗚繃浜嗭紙recordAccept-hungup杩欎釜涓昏鏄粰"閫氳瘽璁板綍鐢ㄧ殑锛�"锛�
                 Object cacheObject = redisCache.getCacheObject(phoneCallReqYQVO.getTaskid() + "recordAccept-hungup");
                 if (ObjectUtils.isNotEmpty(cacheObject) && String.valueOf(cacheObject).equals("1")) {
@@ -1846,7 +2183,9 @@
                 //濡傛灉鍙戦�佺姸鎬佷负6锛屽氨璇存槑宸茬粡鎵撳畬浜嗭紝涔熸病鏈夊繀瑕佺户缁線涓嬭蛋浜�
                 if (StringUtils.isNotEmpty(phoneCallReqYQVO.getTaskid())) {
                     ServiceSubtask serviceSubtask = serviceSubtaskMapper.selectServiceSubtaskById(Long.valueOf(phoneCallReqYQVO.getTaskid()));
-                    if (serviceSubtask.getSendstate() == 6L) return new PhoneCallBackYQVO();
+                    if (serviceSubtask != null && serviceSubtask.getSendstate() != null && serviceSubtask.getSendstate() == 6L) {
+                        return new PhoneCallBackYQVO();
+                    }
                 }
 
                 ServiceSubtask serviceSubtask2 = new ServiceSubtask();
@@ -1854,11 +2193,8 @@
                 serviceSubtask2.setSenduuid(phoneCallReqYQVO.getUuid());
                 serviceSubtask2.setId(Long.valueOf(phoneCallReqYQVO.getTaskid()));
                 serviceSubtaskMapper.updateServiceSubtask(serviceSubtask2);
-                Map<String, String> req = new HashMap<>();
-                req.put("uuid", phoneCallReqYQVO.getUuid());
-                req.put("caller", phoneCallReqYQVO.getPhone());
-                HttpUtil.postJsonRequest(hangup, new Gson().toJson(req));
-                redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "- hangup", "1", 2, TimeUnit.MINUTES);
+                //鎸傛柇锛堝箓绛� + 澶辫触閲嶈瘯锛屽紓甯镐笉鍐嶅線澶栨姏锛岄伩鍏嶅洖璋冪洿鎺� 500锛�
+                doHangup(phoneCallReqYQVO.getUuid(), phoneCallReqYQVO.getPhone(), "鎷ㄥ彨鐨勭敤鎴锋蹇�");
                 //鍏堟妸榛樿鐨剅ecordAccept-hungup鍒犻櫎锛屽啀鏇存柊
                 redisCache.deleteObject(phoneCallReqYQVO.getTaskid() + "recordAccept-hungup");
                 redisCache.setCacheObject(phoneCallReqYQVO.getTaskid() + "recordAccept-hungup", "1", 10, TimeUnit.MINUTES);
@@ -1871,36 +2207,53 @@
                 return new PhoneCallBackYQVO();
             }
         }
+        //缁撴潫璇凡缁忎笅鍙戣繃锛岃鏄庢湰娆″璇濆凡缁忔敹灏撅細瀹夋帓涓�娆″厹搴曟寕鏂��
+        //骞冲彴姝e父鎯呭喌涓嬩細鍐嶅洖璋� PlayEventCallback(playstop=true) 璁╂垜浠珛鍒绘寕鏂紱
+        //浣嗗钩鍙板瓧娈电己澶�/涓嶅洖璋冩椂鐢佃瘽浼氫竴鐩存寕鐫�锛屾墍浠ヨ繖閲屾寜缁撴潫璇瓧鏁颁及绠楁挱鏀炬椂闀垮埌鐐瑰厹搴曟寕涓�娆★紙骞傜瓑锛夈��
+        if (isDialogueEnded(phoneCallReqYQVO.getUuid())) {
+            scheduleHangupFallback(phoneCallReqYQVO.getUuid(), phoneCallReqYQVO.getPhone(),
+                    redisCache.getCacheObject(jsyKey(phoneCallReqYQVO.getUuid())));
+        }
         //瀹氫箟涓�涓垎鏁扮殑鍙橀噺
         Boolean aBoolean1 = redisCache.hasKey(phoneCallReqYQVO.getUuid() + "SCORE");
         if (!aBoolean1) redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "SCORE", 0.0, 120, TimeUnit.MINUTES);
         PhoneCallBackYQVO phoneCallBackYQVO = new PhoneCallBackYQVO();
         //channel_create 閫氶亾鍒涘缓鐨勬椂鍊欙紝鍙互鎵ц涓�浜涘叾瀹冩搷浣滐紝璀鍙戜釜鐭俊涔嬬被鐨�;  鎴戜滑鐨勪笟鍔″彲浠ヤ笉鐢ㄧ    PlayEventCallback 杩欎釜鏄挱鏀捐闊崇殑,鏆傛椂鐢ㄤ笉鍒�     End_time()= -1鎴杗ull琛ㄧず褰撳墠鐨刟srtext涓嶆槸涓�鍙ュ畬鏁寸殑璇�
-        if (phoneCallReqYQVO.getOperate().equals("channel_create")) {
+        if ("channel_create".equals(phoneCallReqYQVO.getOperate())) {
             return phoneCallBackYQVO;
         }
         //PlayEventCallback 杩欎釜鏄挱鏀捐闊崇殑    playstart锛氭斁闊冲紑濮嬶紙闂鎾姤寮�濮嬶級    playstop锛� 鏀鹃煶缁撴潫锛堥棶棰樻挱鎶ョ粨鏉燂級
-        if (phoneCallReqYQVO.getOperate().equals("PlayEventCallback")) {
-            String cacheJSY = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "- jsy");
-            if (phoneCallReqYQVO.getOperate().equals("PlayEventCallback") && phoneCallReqYQVO.getPlaystop() == false) {
-                //鍒ゆ柇redis涓槸鍚︽湁缁撴潫璇�
-                if (StringUtils.isEmpty(cacheJSY)) {
+        if ("PlayEventCallback".equals(phoneCallReqYQVO.getOperate())) {
+            //缁撴潫璇槸鍚﹀凡涓嬪彂锛堢敤 hasKey 鍒ゆ柇锛岀粨鏉熻涓虹┖涔熺畻宸叉敹灏撅級
+            boolean dialogueEnded = isDialogueEnded(phoneCallReqYQVO.getUuid());
+            //playstop 鍙兘鏄� null锛屽钩鍙颁篃鍙兘鐢� play_stop锛岀粺涓�鐢� VO 鍒ゆ柇
+            boolean playStop = phoneCallReqYQVO.playStopIsTrue();
+            String cacheJSY = redisCache.getCacheObject(jsyKey(phoneCallReqYQVO.getUuid()));
+            log.info("PlayEventCallback: uuid={}, playstop={}, 鏄惁宸蹭笅鍙戠粨鏉熻={}",
+                    phoneCallReqYQVO.getUuid(), playStop, dialogueEnded);
+            if (!playStop) {
+                //鏀鹃煶寮�濮�
+                if (!dialogueEnded) {
                     redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "PlayEventCallbackPlaystop", false, 120, TimeUnit.MINUTES);
                     log.error("PlayEventCallbackPlaystop鐨勫�间负-------锛歿}", false);
+                } else {
+                    //骞冲彴寮�濮嬫挱缁撴潫璇紝鍏滃簳鎸傛柇鍦ㄦ柟娉曞叆鍙e凡缁忓畨鎺掕繃锛岃繖閲屽彧鐣欐棩蹇椾究浜庢帓鏌�
+                    log.info("缁撴潫璇紑濮嬫挱鏀�, uuid={}, 缁撴潫璇�={}", phoneCallReqYQVO.getUuid(), cacheJSY);
                 }
             } else {
-                //濡傛灉缁撴潫璇笉涓虹┖锛屽垯瑕佹寕鐢佃瘽浜�
-                if (StringUtils.isNotEmpty(cacheJSY)) {
-                    Map<String, String> req = new HashMap<>();
-                    req.put("uuid", phoneCallReqYQVO.getUuid());
-                    req.put("caller", phoneCallReqYQVO.getPhone());
-//                    HttpUtils.sendPost(hangup, new Gson().toJson(req));
-                    HttpUtil.postJsonRequest(hangup, new Gson().toJson(req));
-                    ServiceSubtask serviceSubtask = serviceSubtaskMapper.selectServiceSubtaskById(Long.valueOf(phoneCallReqYQVO.getTaskid()));
-                    //璁板綍鐢佃瘽鍙戦�佺姸鎬�
-                    setFailPreachForm(serviceSubtask, "3", "閫氳瘽姝e父缁撴潫", "9");
-                    //鍒犻櫎缁撴潫璇殑缂撳瓨
-                    redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "- jsy");
+                //鏀鹃煶缁撴潫
+                if (dialogueEnded) {
+                    //缁撴潫璇挱瀹岋紝绔嬪嵆鎸傛柇锛堝箓绛� + 澶辫触閲嶈瘯锛屽紓甯镐笉澶栨姏锛�
+                    doHangup(phoneCallReqYQVO.getUuid(), phoneCallReqYQVO.getPhone(), "缁撴潫璇挱鏀剧粨鏉�");
+                    //璁板綍鐢佃瘽鍙戦�佺姸鎬侊細鎸傛柇鎴愯触閮借钀藉簱锛屼笉鑳藉啀璁╁洖璋冪洿鎺� 500
+                    if (StringUtils.isNotEmpty(phoneCallReqYQVO.getTaskid())) {
+                        try {
+                            ServiceSubtask serviceSubtask = serviceSubtaskMapper.selectServiceSubtaskById(Long.valueOf(phoneCallReqYQVO.getTaskid()));
+                            setFailPreachForm(serviceSubtask, "3", "閫氳瘽姝e父缁撴潫", "9");
+                        } catch (Exception e) {
+                            log.error("鎸傛柇鍚庢洿鏂板瓙浠诲姟鐘舵�佸け璐�, taskid={}", phoneCallReqYQVO.getTaskid(), e);
+                        }
+                    }
                 } else {
                     redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "PlayEventCallbackPlaystop", true, 120, TimeUnit.MINUTES);
                     log.error("-------PlayEventCallbackPlaystop鐨勫�间负锛歿}", true);
@@ -1920,6 +2273,10 @@
 
         //閫氳繃瀛愪换鍔D鑾峰彇鍒版ā鏉夸俊鎭�
         ServiceSubtask serviceSubtask = serviceSubtaskMapper.selectServiceSubtaskById(Long.valueOf(phoneCallReqYQVO.getTaskid()));
+        if (serviceSubtask == null) {
+            log.warn("鍥炶皟瀵瑰簲鐨勫瓙浠诲姟涓嶅瓨鍦紝鐩存帴蹇界暐, taskid={}, operate={}", phoneCallReqYQVO.getTaskid(), phoneCallReqYQVO.getOperate());
+            return phoneCallBackYQVO;
+        }
         IvrTaskTemplate ivrTaskTemplate = ivrTaskTemplateService.selectIvrTaskTemplateByID(serviceSubtask.getTemplateid());
         //鑾峰彇妯℃澘闂
         IvrTaskTemplateScript ivrTaskTemplateScript = new IvrTaskTemplateScript();
@@ -1928,8 +2285,24 @@
         //鑾峰彇闂ID 鍜� 搴忓彿
         String scriptId = redisCache.getCacheObject(phoneCallReqYQVO.getTaskid().trim() + "-" + phoneCallReqYQVO.getPhone().trim());
         log.error("scriptId鏄灏戯細{}", scriptId);
+        if (StringUtils.isEmpty(scriptId)) {
+            //褰撳墠娌℃湁"姝e湪闂殑闂"锛岃鏄庢湰娆¢�氳瘽宸茬粡鏀跺熬锛堢粨鏉熻宸蹭笅鍙戝悗锛屽钩鍙拌繕浼氳ˉ Silent/Asr 鍥炶皟锛夛紝
+            //鍘熸潵杩欓噷鐩存帴 Long.valueOf(null) 浼氭姏 NumberFormatException 璁╁洖璋� 500銆�
+            //杩欓噷鐩存帴蹇界暐锛屽苟纭繚鍏滃簳鎸傛柇宸茬粡鎺掍笂锛岄伩鍏嶇數璇濈户缁寕鐫�銆�
+            log.warn("鏈彇鍒板綋鍓嶉棶棰業D锛屾寜閫氳瘽宸叉敹灏惧鐞�, taskid={}, phone={}, operate={}",
+                    phoneCallReqYQVO.getTaskid(), phoneCallReqYQVO.getPhone(), phoneCallReqYQVO.getOperate());
+            if (isDialogueEnded(phoneCallReqYQVO.getUuid())) {
+                scheduleHangupFallback(phoneCallReqYQVO.getUuid(), phoneCallReqYQVO.getPhone(),
+                        redisCache.getCacheObject(jsyKey(phoneCallReqYQVO.getUuid())));
+            }
+            return phoneCallBackYQVO;
+        }
         //褰撳墠棰樼殑淇℃伅
         IvrTaskTemplateScriptVO ivrTaskTemplateScriptVO = iIvrTaskTemplateScriptService.getTaskTempScriptInfoByid(Long.valueOf(scriptId));
+        if (ivrTaskTemplateScriptVO == null) {
+            log.warn("褰撳墠闂涓嶅瓨鍦紝蹇界暐鍥炶皟, taskid={}, scriptId={}", phoneCallReqYQVO.getTaskid(), scriptId);
+            return phoneCallBackYQVO;
+        }
         //鍒ゆ柇UUID鏄惁瀛樺湪
         Boolean aBoolean = redisCache.hasKey(phoneCallReqYQVO.getUuid());
         if (!aBoolean) {
@@ -2202,9 +2575,10 @@
                             redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "PlayEventCallbackPlaystop");
 //                        //鑾峰彇涓嬩竴棰�
                             log.error("鑾峰彇涓嬩竴棰樼殑淇℃伅锛歿}", ivrTaskTemplateScriptVO);
-                            if (ivrTaskTemplateScriptVO.getBranchFlag().equals("1") || ivrTaskTemplateScriptVO.getBranchFlag().equals("0") && ivrTaskTemplateScriptVO.getNextScriptno() != null && ivrTaskTemplateScriptVO.getNextScriptno() != 0) {
+                            //branchFlag 鍙兘涓� null锛岀粺涓�鎸夊垎鏀�0澶勭悊锛岄伩鍏� NPE 鎶婂洖璋冩墦 500
+                            if ("1".equals(ivrTaskTemplateScriptVO.getBranchFlag()) || "0".equals(ivrTaskTemplateScriptVO.getBranchFlag()) && ivrTaskTemplateScriptVO.getNextScriptno() != null && ivrTaskTemplateScriptVO.getNextScriptno() != 0) {
                                 Long nextQuestion = null;
-                                if (ivrTaskTemplateScriptVO.getBranchFlag().equals("1")) {
+                                if ("1".equals(ivrTaskTemplateScriptVO.getBranchFlag())) {
                                     nextQuestion = ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j).getNextQuestion();
                                     //鏇存柊鍒嗘暟
                                     double score = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "SCORE");
@@ -2330,7 +2704,8 @@
                     if (ObjectUtils.isNotEmpty(countObj)) count = (Integer) countObj;
                     if (count != null && count >= ivrTaskTemplate.getMateNum()) {
                         //濡傛灉count宸茬粡澶т簬鎴栫瓑浜庢病鏈夊尮閰嶆鏁�
-                        if (ivrTaskTemplateScriptVO.getBranchFlag().equals("0") && ivrTaskTemplateScriptVO.getNextScriptno() == null || ivrTaskTemplateScriptVO.getBranchFlag().equals("0") && ivrTaskTemplateScriptVO.getNextScriptno() == 0 || ivrTaskTemplateScriptVO.getBranchFlag().equals("1") && ivrTaskTemplateScriptVO.getNextScriptno() == null || ivrTaskTemplateScriptVO.getBranchFlag().equals("1") && ivrTaskTemplateScriptVO.getNextScriptno() == 0) {
+                        //branchFlag 鍙兘涓� null锛岀粺涓�鎸夊垎鏀�0澶勭悊锛岄伩鍏� NPE 鎶婂洖璋冩墦 500
+                        if ("0".equals(ivrTaskTemplateScriptVO.getBranchFlag()) && ivrTaskTemplateScriptVO.getNextScriptno() == null || "0".equals(ivrTaskTemplateScriptVO.getBranchFlag()) && ivrTaskTemplateScriptVO.getNextScriptno() == 0 || "1".equals(ivrTaskTemplateScriptVO.getBranchFlag()) && ivrTaskTemplateScriptVO.getNextScriptno() == null || "1".equals(ivrTaskTemplateScriptVO.getBranchFlag()) && ivrTaskTemplateScriptVO.getNextScriptno() == 0) {
                             //濡傛灉鏄渶鍚庝竴閬撻锛屾垨鑰呮病鏈変笅涓�棰樹簡锛屽氨鐩存帴鎸傛満
                             ServiceTask serviceTask1 = serviceTaskService.selectServiceTaskByTaskid(serviceSubtask.getTaskid());
                             phoneCallBackYQVO.setType("text");
@@ -2760,7 +3135,9 @@
     @Override
     public Integer recordAccept(PhoneCallRecordVO phoneCallRecordVO) {
         Object cacheObject = redisCache.getCacheObject(phoneCallRecordVO.getTaskid() + "recordAccept-hungup");
-        if (phoneCallRecordVO.getHangup_cause().equals("NO_USER_RESPONSE") || ObjectUtils.isNotEmpty(cacheObject) && String.valueOf(cacheObject).equals("1")) {
+        // hangup_cause 骞冲彴涓嶄竴瀹氭瘡娆¢兘甯︼紝杩欓噷鍒ょ┖锛岄伩鍏嶆暣鏉¢�氳瘽璁板綍鍥炶皟鐩存帴 NPE 500
+        boolean noUserResponse = "NO_USER_RESPONSE".equals(phoneCallRecordVO.getHangup_cause());
+        if (noUserResponse || ObjectUtils.isNotEmpty(cacheObject) && String.valueOf(cacheObject).equals("1")) {
             //鏃犱汉鎺ュ惉
             //璁板綍鐢佃瘽鍙戦�佺姸鎬�
             ServiceSubtask serviceSubtask = serviceSubtaskMapper.selectServiceSubtaskById(Long.valueOf(phoneCallRecordVO.getTaskid()));
@@ -2773,9 +3150,13 @@
 //        if (phoneCallRecordVO.getAnswered() != null && phoneCallRecordVO.getAnswered() == true) {
             log.error("鏈変汉鎺ョ數璇濅簡锛屽苟涓斿畬浜�");
             //璇存槑鏈変汉鎺ヤ簡
-            int startIndex = phoneCallRecordVO.getRecord_path().indexOf("voice") + "voice".length() + 1;  // 鍔�1鏄烦杩�/绗﹀彿
-            int endIndex = phoneCallRecordVO.getRecord_path().lastIndexOf("/");
-            String result = phoneCallRecordVO.getRecord_path().substring(startIndex, endIndex);
+            //褰曢煶璺緞鍙仛鏃ュ織鐣欑棔锛氬钩鍙扮粰绌�/鏍煎紡涓嶇鏃跺師鏉ョ殑 substring 浼氭姏寮傚父锛屽鑷存暣鏉″洖璋� 500銆佺姸鎬佸拰褰曢煶閮戒笉澶勭悊
+            String recordPath = phoneCallRecordVO.getRecord_path();
+            if (StringUtils.isNotEmpty(recordPath) && recordPath.contains("voice") && recordPath.lastIndexOf("/") > 0) {
+                log.info("閫氳瘽褰曢煶璺緞, taskid={}, record_path={}", phoneCallRecordVO.getTaskid(), recordPath);
+            } else {
+                log.warn("閫氳瘽璁板綍鍥炶皟鏈彇鍒版湁鏁堝綍闊宠矾寰�, taskid={}, record_path={}", phoneCallRecordVO.getTaskid(), recordPath);
+            }
             ServiceSubtask serviceSubtask = new ServiceSubtask();
             serviceSubtask.setRemark("鐢佃瘽鎷ㄦ墦宸插畬鎴�");
             serviceSubtask.setSendstate(6L);
@@ -3062,6 +3443,8 @@
         ssRate.setOrgid(serviceSubtaskCountReq.getOrgid());
         ssRate.setStarttime(serviceSubtaskCountReq.getStartTime());
         ssRate.setEndtime(serviceSubtaskCountReq.getEndTime());
+        ssRate.setStartOutHospTime(serviceSubtaskCountReq.getHospitalStartTime());
+        ssRate.setEndOutHospTime(serviceSubtaskCountReq.getHospitalEndTime());
         ssRate.setVisitCount(serviceSubtaskCountReq.getVisitCount());
         ssRate.setServiceTypeList(serviceSubtaskCountReq.getServiceType());
         if (serviceSubtaskCountReq.getEndTime() != null && new Date().before(serviceSubtaskCountReq.getEndTime())) {
@@ -3153,6 +3536,8 @@
             if (serviceSubtaskCountReq.getEndTime() != null && new Date().before(serviceSubtaskCountReq.getEndTime())) {
                 ss.setEndtime(new Date());
             }
+            ss.setStartOutHospTime(serviceSubtaskCountReq.getHospitalStartTime());
+            ss.setEndOutHospTime(serviceSubtaskCountReq.getHospitalEndTime());
             Double jsRate = serviceSubtaskMapper.selectTimelyRate(ss);
             statistic.setRate(jsRate);
             if (ObjectUtils.isNotEmpty(statistic)) {
@@ -3305,6 +3690,12 @@
                 //搴旈殢璁夸汉娆�
                 if (serviceSubtask.getSendstate() != null && serviceSubtask.getSendstate() != 4) {
                     serviceSubtaskStatistic.setFollowUpNeeded(serviceSubtaskStatistic.getFollowUpNeeded() + 1L);
+                }
+
+                //浜哄伐宸叉嫧閫氭�婚噺
+                if (ObjectUtils.isNotEmpty(serviceSubtask.getIsManuallyCalled())
+                        && serviceSubtask.getIsManuallyCalled().equals(1L)) {
+                    serviceSubtaskStatistic.setManuallyCalled(serviceSubtaskStatistic.getManuallyCalled() + 1L);
                 }
 
                 //棣栨鍑洪櫌闅忚
@@ -4326,6 +4717,7 @@
      * taskSituation4Info - taskSituation = 4
      * taskSituation5Info - taskSituation = 5
      * taskSituation6Info - taskSituation = 7
+     * manuallyCalledInfo - isManuallyCalled = 1L
      * filterCountList - taskSituation = 6 && sendstate = 4
      *
      */
@@ -4398,7 +4790,7 @@
                 serviceSubtaskCountReq.setVisitCount(1);
                 serviceSubtaskCountReq.setCurrentPreachform("4");
                 break;
-            case "wechatInfo":
+            case "weChatInfo":
                 serviceSubtaskCountReq.setVisitCount(1);
                 serviceSubtaskCountReq.setCurrentPreachform("5");
                 break;
@@ -4417,7 +4809,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");
@@ -4454,7 +4846,7 @@
                 serviceSubtaskCountReq.setVisitCount(2);
                 serviceSubtaskCountReq.setCurrentPreachform("4");
                 break;
-            case "wechatAgainInfo":
+            case "weChatAgainInfo":
                 serviceSubtaskCountReq.setVisitCount(2);
                 serviceSubtaskCountReq.setCurrentPreachform("5");
                 break;
@@ -4473,7 +4865,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");
@@ -4503,6 +4895,9 @@
             case "filterCountList":
                 serviceSubtaskCountReq.setTaskSituation(6);
                 serviceSubtaskCountReq.setSendstates(new ArrayList<>(Collections.singletonList(4L)));
+                break;
+            case "manuallyCalledInfo":
+                serviceSubtaskCountReq.setIsManuallyCalled(1L);
                 break;
             default:
                 break;
@@ -5141,12 +5536,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);
@@ -5191,6 +5580,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<>();

--
Gitblit v1.9.3