Jelajahi Sumber

Merge remote-tracking branch 'origin/master'

yjwang 1 bulan lalu
induk
melakukan
6532f0a39f

+ 117 - 0
fs-ai-call-task/src/main/java/com/fs/app/service/CallTaskService.java

@@ -1,8 +1,13 @@
 package com.fs.app.service;
 
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.core.redis.RedisCacheT;
+import com.fs.company.domain.CompanyVoiceRobotic;
+import com.fs.company.mapper.CompanyVoiceRoboticMapper;
 import com.fs.company.param.ExecutionContext;
+import com.fs.company.param.PauseRoboticActiveParam;
 import com.fs.company.service.*;
 import com.fs.company.service.impl.call.node.AiCallTaskNode;
 import lombok.RequiredArgsConstructor;
@@ -12,6 +17,7 @@ import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
+import java.time.LocalTime;
 import java.util.*;
 import java.util.concurrent.*;
 
@@ -29,6 +35,7 @@ public class CallTaskService {
     private final RedisCache redisCache2;
     private final CompanyWorkflowEngine companyWorkflowEngine;
     private final ICompanyVoiceRoboticService companyVoiceRoboticService;
+    private final CompanyVoiceRoboticMapper companyVoiceRoboticMapper;
     @Autowired
     @Qualifier("cidWorkFlowExecutor")
     private Executor cidWorkFlowExecutor;
@@ -81,4 +88,114 @@ public class CallTaskService {
         });
         log.info("===========工作流延时任务扫描结束===========");
     }
+
+    /**
+     * 检查任务状态来暂停外呼任务的外呼运行
+     */
+    public void taskStatusChange(){
+        LocalTime now = LocalTime.now();
+        log.info("===========任务状态变更扫描开始, 当前时间: {}, 分组: {}===========", now, cidGroupNo);
+        pauseOutOfRangeTasks(now);
+        resumeInRangeTasks(now);
+        log.info("===========任务状态变更扫描结束===========");
+    }
+
+    private void pauseOutOfRangeTasks(LocalTime now) {
+        List<CompanyVoiceRobotic> activeTasks = companyVoiceRoboticMapper.selectList(
+                new QueryWrapper<CompanyVoiceRobotic>()
+                        .eq("task_status", 1)
+                        .eq("task_type", 1)
+                        .eq("cid_group_no", cidGroupNo)
+                        .eq("del_flag", 0)
+        );
+        log.info("扫描到执行中的普通任务 {} 个", activeTasks.size());
+        if (activeTasks.isEmpty()) {
+            return;
+        }
+        List<CompletableFuture<Void>> futures = new ArrayList<>();
+        for (CompanyVoiceRobotic task : activeTasks) {
+            if (isOutOfRuntimeRange(task, now)) {
+                log.info("任务[{}]运行时间范围[{},{}),当前{}超出范围,准备自动暂停", task.getId(), task.getRuntimeRangeStart(), task.getRuntimeRangeEnd(), now);
+                futures.add(CompletableFuture.runAsync(() -> {
+                    try {
+                        PauseRoboticActiveParam param = new PauseRoboticActiveParam();
+                        param.setTaskId(task.getId());
+                        param.setActiveType(1);
+                        companyVoiceRoboticService.pauseRoboticActive(param);
+                        companyVoiceRoboticMapper.update(null,
+                                new LambdaUpdateWrapper<CompanyVoiceRobotic>()
+                                        .eq(CompanyVoiceRobotic::getId, task.getId())
+                                        .set(CompanyVoiceRobotic::getPauseSource, "auto")
+                        );
+                        log.info("任务[{}]超出运行时间范围,已自动暂停", task.getId());
+                    } catch (Exception e) {
+                        log.error("自动暂停任务[{}]异常", task.getId(), e);
+                    }
+                }, cidWorkFlowExecutor));
+            }
+        }
+        if (!futures.isEmpty()) {
+            log.info("本次需自动暂停 {} 个任务,等待并行执行完成", futures.size());
+            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
+        }
+    }
+
+    private void resumeInRangeTasks(LocalTime now) {
+        List<CompanyVoiceRobotic> pausedTasks = companyVoiceRoboticMapper.selectList(
+                new QueryWrapper<CompanyVoiceRobotic>()
+                        .eq("task_status", 2)
+                        .eq("task_type", 1)
+                        .eq("pause_source", "auto")
+                        .eq("cid_group_no", cidGroupNo)
+                        .eq("del_flag", 0)
+        );
+        log.info("扫描到自动暂停中的普通任务 {} 个", pausedTasks.size());
+        if (pausedTasks.isEmpty()) {
+            return;
+        }
+        List<CompletableFuture<Void>> futures = new ArrayList<>();
+        for (CompanyVoiceRobotic task : pausedTasks) {
+            if (isWithinRuntimeRange(task, now)) {
+                log.info("任务[{}]运行时间范围[{},{}),当前{}在范围内,准备自动恢复", task.getId(), task.getRuntimeRangeStart(), task.getRuntimeRangeEnd(), now);
+                futures.add(CompletableFuture.runAsync(() -> {
+                    try {
+                        PauseRoboticActiveParam param = new PauseRoboticActiveParam();
+                        param.setTaskId(task.getId());
+                        param.setActiveType(2);
+                        companyVoiceRoboticService.pauseRoboticActive(param);
+                        companyVoiceRoboticMapper.update(null,
+                                new LambdaUpdateWrapper<CompanyVoiceRobotic>()
+                                        .eq(CompanyVoiceRobotic::getId, task.getId())
+                                        .set(CompanyVoiceRobotic::getPauseSource, null)
+                        );
+                        log.info("任务[{}]回到运行时间范围,已自动恢复", task.getId());
+                    } catch (Exception e) {
+                        log.error("自动恢复任务[{}]异常", task.getId(), e);
+                    }
+                }, cidWorkFlowExecutor));
+            }
+        }
+        if (!futures.isEmpty()) {
+            log.info("本次需自动恢复 {} 个任务,等待并行执行完成", futures.size());
+            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
+        }
+    }
+
+    private boolean isOutOfRuntimeRange(CompanyVoiceRobotic task, LocalTime now) {
+        LocalTime start = task.getRuntimeRangeStart();
+        LocalTime end = task.getRuntimeRangeEnd();
+        if (start == null || end == null) {
+            return false;
+        }
+        if (start.isBefore(end)) {
+            return now.isBefore(start) || now.isAfter(end);
+        } else {
+            return now.isAfter(end) && now.isBefore(start);
+        }
+    }
+
+    private boolean isWithinRuntimeRange(CompanyVoiceRobotic task, LocalTime now) {
+        return !isOutOfRuntimeRange(task, now);
+    }
+
 }

+ 19 - 0
fs-ai-call-task/src/main/java/com/fs/app/task/Task.java

@@ -63,4 +63,23 @@ public class Task {
         }
 
     }
+
+    @Scheduled(cron = "0 0/1 * * * ?")
+    public void taskStatusChange(){
+        // 防御性检查:确保依赖注入成功
+        if (tenantTaskRunner == null) {
+            log.error("[Task] tenantTaskRunner 未注入,请检查 Spring 配置");
+            return;
+        }
+        if (taskService == null) {
+            log.error("[Task] taskService 未注入,请检查 Spring 配置");
+            return;
+        }
+        if (saasTaskEnabled) {
+            tenantTaskRunner.runForResponsibleTenant("taskStatusChange", () -> taskService.taskStatusChange());
+        } else {
+            taskService.taskStatusChange();
+        }
+
+    }
 }

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

@@ -55,6 +55,7 @@ public class CompanyVoiceRoboticCallLogCallphoneController extends BaseControlle
                 companyVoiceRoboticCallLogCallphone.setCallerIds(calleeIds);
                 startPage();
                 List<CompanyVoiceRoboticCallLogCallphone> list = companyVoiceRoboticCallLogCallphoneService.selectCompanyVoiceRoboticCallLogCallphoneListData(companyVoiceRoboticCallLogCallphone);
+
                 return getDataTable(list);
             } else {
                 return getDataTable(new ArrayList<>());

+ 8 - 0
fs-company/src/main/java/com/fs/company/controller/crm/CrmCustomerController.java

@@ -80,6 +80,10 @@ public class CrmCustomerController extends BaseController
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         PageHelper.startPage(param.getPageNum(), param.getPageSize());
         param.setCompanyId(loginUser.getCompany().getCompanyId());
+        if(!StringUtils.isEmpty(param.getEncryptedMobile())){
+            param.setMobile(PhoneUtil.encryptPhone(param.getEncryptedMobile()));
+            param.setEncryptedMobile(null);
+        }
         List<CrmLineCustomerListQueryVO> list = crmCustomerService.selectCrmLineCustomerListQuery(param);
         if (list != null) {
             for (CrmLineCustomerListQueryVO vo : list) {
@@ -195,6 +199,10 @@ public class CrmCustomerController extends BaseController
         if(!StringUtils.isEmpty(param.getCreateTimeRange())){
             param.setCustomerCreateTime(param.getCreateTimeRange().split("--"));
         }
+        if(!StringUtils.isEmpty(param.getEncryptedMobile())){
+            param.setMobile(PhoneUtil.encryptPhone(param.getEncryptedMobile()));
+            param.setEncryptedMobile(null);
+        }
         List<CrmMyCustomerListQueryVO> list = crmCustomerService.selectCrmMyCustomerListQuery(param);
         if (list != null) {
             for (CrmMyCustomerListQueryVO vo : list) {

+ 3 - 3
fs-qw-api/src/main/java/com/fs/app/controller/QwFriendWelcomeController.java

@@ -46,7 +46,7 @@ public class QwFriendWelcomeController extends BaseController
      * 新增好友欢迎语
      * SaaS 多租户支持:通过 tenantId 切换租户数据源
      */
-    @Log(title = "好友欢迎语", businessType = BusinessType.INSERT)
+//    @Log(title = "好友欢迎语", businessType = BusinessType.INSERT)
     @PostMapping
     public R add(@RequestBody QwFriendWelcomeParam qwFriendWelcomeParam, @RequestParam("tenantId") Long tenantId)
     {
@@ -69,7 +69,7 @@ public class QwFriendWelcomeController extends BaseController
      * 修改好友欢迎语
      * SaaS 多租户支持:通过 tenantId 切换租户数据源
      */
-    @Log(title = "好友欢迎语", businessType = BusinessType.UPDATE)
+//    @Log(title = "好友欢迎语", businessType = BusinessType.UPDATE)
     @PostMapping("/update")
     public R edit(@RequestBody QwFriendWelcome qwFriendWelcome, @RequestParam("tenantId") Long tenantId)
     {
@@ -92,7 +92,7 @@ public class QwFriendWelcomeController extends BaseController
      * 删除好友欢迎语
      * SaaS 多租户支持:通过 tenantId 切换租户数据源
      */
-    @Log(title = "好友欢迎语", businessType = BusinessType.DELETE)
+//    @Log(title = "好友欢迎语", businessType = BusinessType.DELETE)
 	@DeleteMapping("/{ids}")
     public AjaxResult remove(@PathVariable Long[] ids, @RequestParam("tenantId") Long tenantId)
     {

+ 1 - 0
fs-service/src/main/java/com/fs/company/domain/CompanyVoiceRobotic.java

@@ -104,6 +104,7 @@ public class CompanyVoiceRobotic {
     private String runTaskFlow;
     // 任务状态0待执行1执行中2执行中断3执行完成
     private Integer taskStatus;
+    private String pauseSource;
     private Integer addWxTime;
     /** 创建人 */
     @Excel(name = "创建时间")

+ 2 - 1
fs-service/src/main/java/com/fs/company/service/impl/CompanyVoiceRoboticCallLogCallphoneServiceImpl.java

@@ -29,6 +29,7 @@ import com.fs.company.vo.CompanyVoiceRoboticCallLogCount;
 import com.fs.company.vo.easycall.EasyCallCallPhoneVO;
 import com.fs.core.config.TenantConfigContext;
 import com.fs.crm.service.ICrmCustomerPropertyService;
+import com.fs.his.utils.PhoneUtil;
 import com.fs.qw.domain.QwUser;
 import com.fs.qw.mapper.QwUserMapper;
 import com.fs.sensitive.DTO.AgentSensitiveWordDetectResultDTO;
@@ -357,7 +358,7 @@ public class CompanyVoiceRoboticCallLogCallphoneServiceImpl extends ServiceImpl<
                     companyVoiceRoboticCallLog.setRecordPath(result.getWavfile());
                     companyVoiceRoboticCallLog.setCallType(2);
                     companyVoiceRoboticCallLog.setContentList(result.getDialogue());
-                    companyVoiceRoboticCallLog.setCallerNum(result.getTelephone());
+                    companyVoiceRoboticCallLog.setCallerNum(PhoneUtil.encryptPhone(result.getTelephone()));
                     companyVoiceRoboticCallLog.setCalleeNum(result.getCallerNumber());
                     companyVoiceRoboticCallLog.setUuid(result.getUuid());
                     Long createTime = result.getCalloutTime();

+ 4 - 7
fs-service/src/main/java/com/fs/company/service/impl/CompanyVoiceRoboticServiceImpl.java

@@ -1431,6 +1431,8 @@ public class CompanyVoiceRoboticServiceImpl extends ServiceImpl<CompanyVoiceRobo
         final Long workflowId = robotic.getCompanyAiWorkflowId();
         final Long roboticId = robotic.getId();
 
+        companyWorkflowEngine.createSipTask(roboticId, workflowId);
+
         // 先初始化所有工作流实例
         List<ExecutionResult> initResults = new ArrayList<>();
         for (CompanyVoiceRoboticBusiness business : roboticBusinesseList) {
@@ -1973,27 +1975,22 @@ public class CompanyVoiceRoboticServiceImpl extends ServiceImpl<CompanyVoiceRobo
                 return R.error("任务不在执行中状态,无法暂停");
             }
             robotic.setTaskStatus(2);
+            robotic.setPauseSource("manual");
             updateCompanyVoiceRobotic(robotic);
-            // 更新Redis缓存
             redisCache2.setCacheObject("task:status:" + param.getTaskId(), 2);
-            // 调用EasyCall暂停
             pauseEasyCallTask(robotic);
             return R.ok("暂停成功");
         }
-        //恢复任务继续进入可运行
         else if (ACTIVE_TYPE_CONTINUE.equals(param.getActiveType())) {
             CompanyVoiceRobotic robotic = selectCompanyVoiceRoboticById(param.getTaskId());
             if (robotic == null || robotic.getTaskStatus() != 2) {
                 return R.error("任务不在中断状态,无法继续");
             }
             robotic.setTaskStatus(1);
+            robotic.setPauseSource(null);
             updateCompanyVoiceRobotic(robotic);
-            // 更新Redis缓存
             redisCache2.setCacheObject("task:status:" + param.getTaskId(), 1);
-            // 调用EasyCall恢复
             resumeEasyCallTask(robotic);
-            // 异步执行恢复扫描(分批处理,避免阻塞接口)
-            // 通过SpringUtils获取代理对象调用,确保@Async生效
             SpringUtils.getBean(CompanyVoiceRoboticServiceImpl.class).resumePausedInstances(param.getTaskId());
             return R.ok("继续成功");
         }

+ 33 - 18
fs-service/src/main/java/com/fs/company/service/impl/CompanyWorkflowEngineImpl.java

@@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fs.common.core.redis.RedisCache;
 import com.fs.common.exception.CustomException;
 import com.fs.common.utils.StringUtils;
 import com.fs.company.domain.*;
@@ -29,6 +30,7 @@ import org.springframework.stereotype.Service;
 import java.time.LocalDateTime;
 import java.time.LocalTime;
 import java.util.*;
+import java.util.concurrent.TimeUnit;
 
 /**
  * @author MixLiu
@@ -75,6 +77,9 @@ public class CompanyWorkflowEngineImpl implements CompanyWorkflowEngine {
     @Autowired
     CompanyWxClientMapper companyWxClientMapper;
 
+    @Autowired
+    private RedisCache redisCache;
+
     @Autowired
     private CloudHostProper cloudHostProper;
 
@@ -102,8 +107,6 @@ public class CompanyWorkflowEngineImpl implements CompanyWorkflowEngine {
                     definition.getStartNodeKey(), context, definition);
 
             log.info("工作流初始化成功: {} -> {}", workflowInstanceId, workflowDefinitionId);
-            //为任务创建sip任务并存入表数据
-            createSipTask(Long.parseLong(inputVariables.get("roboticId").toString()),workflowDefinitionId);
             return ExecutionResult.success()
                     .nextNodeKey(definition.getStartNodeKey())
                     .workflowInstanceId(workflowInstanceId).build();
@@ -572,35 +575,44 @@ public class CompanyWorkflowEngineImpl implements CompanyWorkflowEngine {
      * @param workFlowId
      */
     public Long createSipTask(Long roboticId, Long workFlowId) {
-        try {
-            List<String> nodeTypes = Arrays.asList(NodeTypeEnum.AI_CALL_TASK.getCode());
-            CompanyVoiceRobotic robotic = companyVoiceRoboticMapper.selectCompanyVoiceRoboticById(roboticId);
-            List<CompanyWorkflowNode> companyWorkflowNodes = companyWorkflowNodeMapper.selectNodesByWorkflowIdAndTypes(workFlowId, nodeTypes);
-            //为所有外呼节点创建任务的对应sip外呼任务
-            for (CompanyWorkflowNode callNode : companyWorkflowNodes) {
+        List<String> nodeTypes = Arrays.asList(NodeTypeEnum.AI_CALL_TASK.getCode());
+        CompanyVoiceRobotic robotic = companyVoiceRoboticMapper.selectCompanyVoiceRoboticById(roboticId);
+        List<CompanyWorkflowNode> companyWorkflowNodes = companyWorkflowNodeMapper.selectNodesByWorkflowIdAndTypes(workFlowId, nodeTypes);
+        for (CompanyWorkflowNode callNode : companyWorkflowNodes) {
+            String lockKey = "sipTask:lock:" + roboticId + ":" + callNode.getNodeKey();
+            boolean locked = redisCache.setIfAbsent(lockKey, "1", 30, TimeUnit.SECONDS);
+            if (!locked) {
+                log.info("createSipTask: 其他线程正在创建SIP任务,等待后重试 - roboticId: {}, nodeKey: {}", roboticId, callNode.getNodeKey());
+                for (int i = 0; i < 20; i++) {
+                    try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
+                    CompanySiptaskInfo existing = companySiptaskInfoMapper.selectSipTaskInfoByTaskIdAndNodeKey(roboticId, callNode.getNodeKey());
+                    if (existing != null && existing.getBatchId() != null) {
+                        return existing.getBatchId();
+                    }
+                }
+                log.warn("createSipTask: 等待超时,尝试直接创建 - roboticId: {}, nodeKey: {}", roboticId, callNode.getNodeKey());
+            }
+            try {
+                CompanySiptaskInfo existingSipTask = companySiptaskInfoMapper.selectSipTaskInfoByTaskIdAndNodeKey(roboticId, callNode.getNodeKey());
+                if (existingSipTask != null && existingSipTask.getBatchId() != null) {
+                    log.info("createSipTask: SIP任务已存在,跳过创建 - roboticId: {}, nodeKey: {}, batchId: {}", roboticId, callNode.getNodeKey(), existingSipTask.getBatchId());
+                    return existingSipTask.getBatchId();
+                }
                 String nodeConfig = callNode.getNodeConfig();
                 AiCallConfigVO callConfigVo = JSONObject.parseObject(nodeConfig, AiCallConfigVO.class);
                 EasyCallCreateTaskParam createParam = new EasyCallCreateTaskParam();
-                // 任务名称:使用任务名称_工作流id_节点key
                 createParam.setBatchName(cloudHostProper.getCompanyName()+"-"+robotic.getName() + "_" + workFlowId + "_" + callNode.getNodeKey());
                 if (null != callConfigVo.getMaxConcurrency()) {
                     createParam.setThreadNum(Long.valueOf(callConfigVo.getMaxConcurrency()));
                 } else {
                     createParam.setThreadNum(3L);
                 }
-                // AI 外呼模式
                 createParam.setTaskType(1);
-                // 外呼线路(网关)
                 createParam.setGatewayId(callConfigVo.getGatewayId());
-                // 大模型底座
                 createParam.setLlmAccountId(callConfigVo.getLlmAccountId());
-                // 音色编号
                 createParam.setVoiceCode(callConfigVo.getVoiceCode());
-                // 音色来源(如未配置默认留空,由 EasyCallCenter365 使用默认值)
                 createParam.setVoiceSource(callConfigVo.getVoiceSource());
-                // 技能组(转人工客服分组,可选)
                 createParam.setGroupId(callConfigVo.getBusiGroupId());
-                // 模型参数
                 createParam.setTtsModels(callConfigVo.getTtsModels());
 
                 if (callConfigVo.getExtensionList() != null && !callConfigVo.getExtensionList().isEmpty()) {
@@ -628,9 +640,12 @@ public class CompanyWorkflowEngineImpl implements CompanyWorkflowEngine {
                 sipTaskInfo.setTaskJson(JSONObject.toJSONString(task));
                 companySiptaskInfoMapper.insertCompanySiptaskInfo(sipTaskInfo);
                 return task.getBatchId();
+            } catch (Exception ex) {
+                log.error("创建SIP任务失败:{}", ex);
+                return null;
+            } finally {
+                redisCache.deleteObject(lockKey);
             }
-        } catch (Exception ex) {
-            log.error("创建SIP任务失败:{}", ex);
         }
         return null;
     }

+ 23 - 3
fs-service/src/main/java/com/fs/company/service/impl/call/node/AiCallTaskNode.java

@@ -422,9 +422,29 @@ public class AiCallTaskNode extends AbstractWorkflowNode {
         if(null != sipTaskInfo && null != sipTaskInfo.getBatchId()){
             return  sipTaskInfo.getBatchId();
         }
-        //没有的情况下创建任务并返回
-        CompanyAiWorkflowExec companyAiWorkflowExec = companyAiWorkflowExecMapper.selectByWorkflowInstanceId(workflowInstanceId);
-        return companyWorkflowEngine.createSipTask(roboticId, companyAiWorkflowExec.getWorkflowId());
+        String lockKey = "sipTask:lock:" + roboticId + ":" + nodeKey;
+        boolean locked = super.redisCache.setIfAbsent(lockKey, "1", 30, TimeUnit.SECONDS);
+        if (!locked) {
+            log.info("getTaskBatchId: 其他线程正在创建SIP任务,等待后重试 - roboticId: {}, nodeKey: {}", roboticId, nodeKey);
+            for (int i = 0; i < 20; i++) {
+                try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
+                sipTaskInfo = companySiptaskInfoMapper.selectSipTaskInfoByTaskIdAndNodeKey(roboticId, nodeKey);
+                if (null != sipTaskInfo && null != sipTaskInfo.getBatchId()) {
+                    return sipTaskInfo.getBatchId();
+                }
+            }
+            log.warn("getTaskBatchId: 等待超时,尝试直接创建 - roboticId: {}, nodeKey: {}", roboticId, nodeKey);
+        }
+        try {
+            sipTaskInfo = companySiptaskInfoMapper.selectSipTaskInfoByTaskIdAndNodeKey(roboticId, nodeKey);
+            if (null != sipTaskInfo && null != sipTaskInfo.getBatchId()) {
+                return sipTaskInfo.getBatchId();
+            }
+            CompanyAiWorkflowExec companyAiWorkflowExec = companyAiWorkflowExecMapper.selectByWorkflowInstanceId(workflowInstanceId);
+            return companyWorkflowEngine.createSipTask(roboticId, companyAiWorkflowExec.getWorkflowId());
+        } finally {
+            super.redisCache.deleteObject(lockKey);
+        }
     }
 //    @Override
 //    protected void postExecute(ExecutionContext context, ExecutionResult result) {

+ 3 - 0
fs-service/src/main/java/com/fs/crm/param/CrmLineCustomerListQueryParam.java

@@ -25,6 +25,9 @@ public class CrmLineCustomerListQueryParam extends BaseQueryParam
     @Excel(name = "手机")
     private String mobile;
 
+    /** 加密手机 */
+    private String encryptedMobile;
+
     /** 性别 */
     @Excel(name = "性别",readConverterExp = "1=男,2=女")
     private Integer sex;

+ 3 - 0
fs-service/src/main/java/com/fs/crm/param/CrmMyCustomerListQueryParam.java

@@ -27,6 +27,9 @@ public class CrmMyCustomerListQueryParam extends BaseQueryParam
     @Excel(name = "手机")
     private String mobile;
 
+    /** 加密手机 */
+    private String encryptedMobile;
+
     /** 性别 */
     @Excel(name = "性别",readConverterExp = "1=男,2=女")
     private Integer sex;

+ 3 - 2
fs-service/src/main/java/com/fs/qwApi/service/impl/QwApiServiceImpl.java

@@ -646,9 +646,10 @@ public class QwApiServiceImpl implements QwApiService {
         QwCompany qwCompany = iQwCompanyService.selectQwCompanyByCorpId(corpId);
 
 
-        String openSecret = qwCompany.getOpenSecret();
+//        String openSecret = qwCompany.getOpenSecret();
+        String permanentCode = qwCompany.getPermanentCode();
 
-        String token = getToken(corpId, openSecret);
+        String token = getToken(corpId, permanentCode);
 
         URIBuilder uriBuilder = null;
 

+ 3 - 2
fs-service/src/main/resources/db/tenant-initTable.sql

@@ -2645,6 +2645,7 @@ CREATE TABLE `company_voice_robotic`
     `available_start_time`   time NULL DEFAULT NULL COMMENT '应用场景指定时间范围开始',
     `available_end_time`     time NULL DEFAULT NULL COMMENT '应用场景指定时间范围结束',
     `del_flag`               tinyint NULL DEFAULT 0 COMMENT '删除标志 0正常 1删除',
+    `pause_source` varchar(20) NULL DEFAULT NULL COMMENT '暂停来源:auto-自动暂停(超出运行时间范围) manual-手动暂停',
     PRIMARY KEY (`id`) USING BTREE,
     INDEX                    `company_scene_idx`(`company_id`, `scene_type`, `del_flag`) USING BTREE COMMENT '公司,场景索引'
 ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '机器人外呼任务' ROW_FORMAT = DYNAMIC;
@@ -3073,8 +3074,8 @@ CREATE TABLE `crm_customer`
     `historical_communication` text NULL COMMENT '历史沟通内容',
     `effective_customer` tinyint NULL DEFAULT NULL COMMENT '有效客户:1:有效,0或者空:无效',
     `ai_call_remark` varchar(500) NULL DEFAULT NULL COMMENT 'AI外呼备注',
-    `effective_record_path` varchar(1000) NULL DEFAULT NULL COMMENT '最后一次设置有效时录音',
-    `last_effective_callphone_log_id` bigint NULL DEFAULT NULL COMMENT '最后一次设置有效时外呼记录id',
+    `effective_record_path` varchar(1000) NULL DEFAULT NULL COMMENT '最后一次设置录音',
+    `last_effective_callphone_log_id` bigint NULL DEFAULT NULL COMMENT '最后一次设置外呼记录id',
     PRIMARY KEY (`customer_id`) USING BTREE,
     UNIQUE INDEX `customer_code`(`customer_code`) USING BTREE,
     INDEX                   `create_user_id`(`create_user_id`) USING BTREE,

+ 2 - 0
fs-service/src/main/resources/mapper/company/CompanyVoiceRoboticMapper.xml

@@ -27,6 +27,7 @@
         <result property="companyAiWorkflowId"    column="company_ai_workflow_id"    />
         <result property="delFlag"    column="del_flag"    />
         <result property="isWeCom"    column="is_we_com"    />
+        <result property="pauseSource"    column="pause_source"    />
 
     </resultMap>
 
@@ -187,6 +188,7 @@
             <if test="createUser != null">create_user = #{createUser},</if>
             <if test="companyAiWorkflowId != null">company_ai_workflow_id = #{companyAiWorkflowId},</if>
             <if test="taskStatus != null">task_status = #{taskStatus},</if>
+            <if test="pauseSource != null">pause_source = #{pauseSource},</if>
         </trim>
         where id = #{id}
     </update>

+ 1 - 1
fs-service/src/main/resources/mapper/fastGpt/FastGptChatSessionMapper.xml

@@ -29,7 +29,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     </resultMap>
 
     <sql id="selectFastGptChatSessionVo">
-        select session_id,remind_time,is_reply,last_time,remind_status,remind_count,qw_ext_id,qw_user_id,chat_id,is_artificial, user_id, kf_id, create_time, update_time, status, company_id, is_look, user_type, nick_name, avatar,userInfo from fastgpt_chat_session
+        select session_id,remind_time,is_reply,last_time,remind_status,remind_count,qw_ext_id,qw_user_id,chat_id,is_artificial, user_id, kf_id, create_time, update_time, status, company_id, is_look, user_type, nick_name, avatar,user_info from fastgpt_chat_session
     </sql>
 
     <select id="selectFastGptChatSessionList" parameterType="FastGptChatSession" resultMap="FastGptChatSessionResult">