Selaa lähdekoodia

手工外呼修复延迟写表数据拿不到问题

lmx 1 viikko sitten
vanhempi
commit
9e48bc4d2a

+ 100 - 2
fs-company/src/main/java/com/fs/company/controller/aiSipCall/AiSipCallOutboundCdrController.java

@@ -16,6 +16,8 @@ import com.fs.common.exception.ServiceException;
 import com.fs.common.utils.SecurityUtils;
 import com.fs.common.utils.SecurityUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.company.domain.CrmCustomerCallLog;
+import com.fs.company.mapper.CrmCustomerCallLogMapper;
 import com.fs.company.mapper.EasyCallMapper;
 import com.fs.company.mapper.EasyCallMapper;
 import com.fs.company.vo.easycall.EasyCallOutBoundVO;
 import com.fs.company.vo.easycall.EasyCallOutBoundVO;
 import com.fs.framework.datasource.TenantDataSourceManager;
 import com.fs.framework.datasource.TenantDataSourceManager;
@@ -34,6 +36,8 @@ import java.util.Base64;
 import java.util.Date;
 import java.util.Date;
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
 
 
 /**
 /**
  * aiSIP手动外呼通话记录Controller
  * aiSIP手动外呼通话记录Controller
@@ -54,6 +58,8 @@ public class AiSipCallOutboundCdrController extends BaseController
     TenantDataSourceManager tenantDataSourceManager;
     TenantDataSourceManager tenantDataSourceManager;
     @Autowired
     @Autowired
     private TokenService tokenService;
     private TokenService tokenService;
+    @Autowired
+    private CrmCustomerCallLogMapper crmCustomerCallLogMapper;
 
 
     /** XOR加密公钥(与 his_java PhoneUtil.PUBLIC_KEY_STR 保持一致) */
     /** XOR加密公钥(与 his_java PhoneUtil.PUBLIC_KEY_STR 保持一致) */
     private static final String XOR_KEY = "ylrz112233";
     private static final String XOR_KEY = "ylrz112233";
@@ -175,11 +181,18 @@ public class AiSipCallOutboundCdrController extends BaseController
     }
     }
 
 
 
 
+    /** 同步重试最大次数 */
+    private static final int SYNC_RETRY_MAX_TIMES = 3;
+    /** 同步重试间隔(秒) */
+    private static final long SYNC_RETRY_INTERVAL_SECONDS = 10L;
+
     /**
     /**
      * 同步aiSIP外呼通话记录
      * 同步aiSIP外呼通话记录
      * 说明:
      * 说明:
      * 1. 如果有 workflowInstanceId 和 roboticId,走原来的机器人/工作流外呼记录
      * 1. 如果有 workflowInstanceId 和 roboticId,走原来的机器人/工作流外呼记录
      * 2. 如果没有 workflowInstanceId 和 roboticId,走用户手动外呼记录
      * 2. 如果没有 workflowInstanceId 和 roboticId,走用户手动外呼记录
+     * 3. 如果对方通话记录尚未写入,立即返回"正在同步录音中",后台异步重试3次(每次间隔10秒)
+     * 4. 3次重试后仍无数据,则用请求参数写入一条仅含基础信息的CrmCustomerCallLog(不含通话数据),后续可通过uuid手动同步
      */
      */
     @PostMapping("/syncByUuid")
     @PostMapping("/syncByUuid")
     public AjaxResult syncByUuid(@RequestBody ApiCallRecordByUuidQueryParams req) {
     public AjaxResult syncByUuid(@RequestBody ApiCallRecordByUuidQueryParams req) {
@@ -195,10 +208,13 @@ public class AiSipCallOutboundCdrController extends BaseController
         //获取租户id
         //获取租户id
         req.setTenantId(getTenantId());
         req.setTenantId(getTenantId());
         EasyCallOutBoundVO callPhoneRes = easyCallMapper.getOutBoundInfoByUuid(req.getUuid());
         EasyCallOutBoundVO callPhoneRes = easyCallMapper.getOutBoundInfoByUuid(req.getUuid());
+        tenantDataSourceManager.ensureSwitchByTenantId(SecurityUtils.getTenantId());
         if (ObjectUtil.isEmpty(callPhoneRes)) {
         if (ObjectUtil.isEmpty(callPhoneRes)) {
-            return AjaxResult.error("未同步到对应通话记录");
+            // 对方通话记录尚未写入,异步重试
+            log.info("syncByUuid uuid={} 首次未查询到通话记录,启动异步重试", req.getUuid());
+            asyncRetrySyncByUuid(req);
+            return AjaxResult.success("正在同步录音中");
         }
         }
-        tenantDataSourceManager.ensureSwitchByTenantId(SecurityUtils.getTenantId());
         int rows;
         int rows;
         if (req.getWorkflowInstanceId() != null && req.getRoboticId() != null) {
         if (req.getWorkflowInstanceId() != null && req.getRoboticId() != null) {
             // 工作流外呼保存逻辑
             // 工作流外呼保存逻辑
@@ -216,6 +232,88 @@ public class AiSipCallOutboundCdrController extends BaseController
         return AjaxResult.error("未查到对应通话记录或同步失败");
         return AjaxResult.error("未查到对应通话记录或同步失败");
     }
     }
 
 
+    /**
+     * 异步重试同步通话记录
+     * 最多重试3次,每次间隔10秒;若3次均未查到数据,则用请求参数写入一条仅含基础信息的CrmCustomerCallLog
+     */
+    private void asyncRetrySyncByUuid(ApiCallRecordByUuidQueryParams req) {
+        Long tenantId = req.getTenantId();
+        CompletableFuture.runAsync(() -> {
+            EasyCallOutBoundVO callPhoneRes = null;
+            for (int i = 1; i <= SYNC_RETRY_MAX_TIMES; i++) {
+                try {
+                    TimeUnit.SECONDS.sleep(SYNC_RETRY_INTERVAL_SECONDS);
+                } catch (InterruptedException e) {
+                    log.error("syncByUuid uuid={} 重试等待被中断", req.getUuid(), e);
+                    Thread.currentThread().interrupt();
+                    break;
+                }
+                log.info("syncByUuid uuid={} 第{}次重试查询通话记录", req.getUuid(), i);
+                try {
+                    callPhoneRes = easyCallMapper.getOutBoundInfoByUuid(req.getUuid());
+                } catch (Exception e) {
+                    log.error("syncByUuid uuid={} 第{}次重试查询异常", req.getUuid(), i, e);
+                }
+                if (ObjectUtil.isNotEmpty(callPhoneRes)) {
+                    log.info("syncByUuid uuid={} 第{}次重试查询到通话记录", req.getUuid(), i);
+                    break;
+                }
+            }
+            // 切换回租户数据源
+            tenantDataSourceManager.ensureSwitchByTenantId(tenantId);
+            try {
+                if (ObjectUtil.isNotEmpty(callPhoneRes)) {
+                    // 重试成功,走正常同步逻辑
+                    if (req.getWorkflowInstanceId() != null && req.getRoboticId() != null) {
+                        aiSipCallOutboundCdrService.syncByUuid(req, callPhoneRes);
+                    } else {
+                        if (req.getCustomerId() != null) {
+                            aiSipCallOutboundCdrService.syncCrmCustomerCallLogByUuid(req, callPhoneRes);
+                        } else {
+                            log.warn("syncByUuid uuid={} 重试成功但客户ID为空,跳过手动外呼同步", req.getUuid());
+                        }
+                    }
+                    log.info("syncByUuid uuid={} 异步重试同步完成", req.getUuid());
+                } else {
+                    // 3次重试均无数据,用请求参数写入仅含基础信息的CrmCustomerCallLog(不含通话详细数据)
+                    log.warn("syncByUuid uuid={} 重试{}次后仍未查到通话记录,写入基础记录待后续手动同步", req.getUuid(), SYNC_RETRY_MAX_TIMES);
+                    insertBasicCrmCustomerCallLog(req);
+                }
+            } catch (Exception e) {
+                log.error("syncByUuid uuid={} 异步重试同步处理异常", req.getUuid(), e);
+            }
+        });
+    }
+
+    /**
+     * 用请求参数写入仅含基础信息的CrmCustomerCallLog(不含通话数据:录音、内容、时长、费用等)
+     * 后续可通过uuid手动同步补充通话数据
+     */
+    private void insertBasicCrmCustomerCallLog(ApiCallRecordByUuidQueryParams req) {
+        CrmCustomerCallLog callLog = new CrmCustomerCallLog();
+        callLog.setUuid(req.getUuid());
+        callLog.setStatus(req.getStatus());
+        callLog.setCompanyId(req.getCompanyId());
+        callLog.setCompanyUserId(req.getCompanyUserId());
+        callLog.setCustomerId(req.getCustomerId());
+        callLog.setIntention(req.getIntent());
+        callLog.setCreateTime(new Date());
+        callLog.setRunTime(new Date());
+        if (StringUtils.isNotBlank(req.getCallType())) {
+            callLog.setCallType(Integer.valueOf(req.getCallType()));
+        } else {
+            callLog.setCallType(3); // 默认03人工外呼
+        }
+        // 不写通话相关数据:recordPath、contentList、callerNum、calleeNum、callCreateTime、callAnswerTime、callTime、cost、billingMinute
+        // 后续可通过uuid手动同步补充
+        try {
+            crmCustomerCallLogMapper.insertCrmCustomerCallLog(callLog);
+            log.info("syncByUuid uuid={} 写入基础CrmCustomerCallLog记录成功", req.getUuid());
+        } catch (Exception e) {
+            log.error("syncByUuid uuid={} 写入基础CrmCustomerCallLog记录失败", req.getUuid(), e);
+        }
+    }
+
     public static Long getTenantId() {
     public static Long getTenantId() {
         Authentication auth = SecurityContextHolder.getContext().getAuthentication();
         Authentication auth = SecurityContextHolder.getContext().getAuthentication();
         if (auth == null || auth.getPrincipal() == null) {
         if (auth == null || auth.getPrincipal() == null) {

+ 1 - 0
fs-company/src/main/java/com/fs/company/controller/aiSipCall/AiSipCallUserController.java

@@ -226,6 +226,7 @@ public class AiSipCallUserController extends BaseController
         String extNum = param.get("extNum");
         String extNum = param.get("extNum");
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         String ids = aiSipCallUserService.getGateWayIdListByCompanyId(loginUser.getCompany().getCompanyId());
         String ids = aiSipCallUserService.getGateWayIdListByCompanyId(loginUser.getCompany().getCompanyId());
+//        ids = "5";
         if(StringUtils.isNotBlank(ids)){
         if(StringUtils.isNotBlank(ids)){
             param.put("myGateway",ids);
             param.put("myGateway",ids);
         }
         }