Przeglądaj źródła

租户总后台-ai外呼任务报错处理

Long 1 tydzień temu
rodzic
commit
48a7b4e45f

+ 98 - 3
fs-admin-saas/src/main/java/com/fs/company/controller/CompanyVoiceRoboticController.java

@@ -6,7 +6,10 @@ import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.SecurityUtils;
 import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.aicall.domain.TaskInfo;
+import com.fs.aicall.service.AiCallService;
 import com.fs.company.domain.CompanyVoiceRobotic;
 import com.fs.company.domain.CompanyVoiceRoboticCallees;
 import com.fs.company.domain.CompanyVoiceRoboticWx;
@@ -17,7 +20,8 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
-import java.util.List;
+import java.util.*;
+import java.util.stream.Collectors;
 
 /**
  * 机器人外呼任务Controller(SaaS管理端)
@@ -38,6 +42,9 @@ public class CompanyVoiceRoboticController extends BaseController {
     @Autowired
     private ICompanyVoiceRoboticWxService companyVoiceRoboticWxService;
 
+    @Autowired
+    private AiCallService aiCallService;
+
     /**
      * 查询机器人外呼任务列表(分页)
      */
@@ -114,7 +121,7 @@ public class CompanyVoiceRoboticController extends BaseController {
     /**
      * 新增机器人外呼任务
      */
-    @PreAuthorize("@ss.hasPermi('company:companyVoiceRobotic:add')")
+    @PreAuthorize("@ss.hasPermi('system:companyVoiceRobotic:add')")
     @Log(title = "机器人外呼任务", businessType = BusinessType.INSERT)
     @PostMapping
     public AjaxResult add(@RequestBody CompanyVoiceRobotic companyVoiceRobotic) {
@@ -134,7 +141,7 @@ public class CompanyVoiceRoboticController extends BaseController {
     /**
      * 删除机器人外呼任务
      */
-    @PreAuthorize("@ss.hasPermi('company:companyVoiceRobotic:remove')")
+    @PreAuthorize("@ss.hasPermi('system:companyVoiceRobotic:remove')")
     @Log(title = "机器人外呼任务", businessType = BusinessType.DELETE)
     @DeleteMapping("/{ids}")
     public AjaxResult remove(@PathVariable Long[] ids) {
@@ -149,4 +156,92 @@ public class CompanyVoiceRoboticController extends BaseController {
     public R companyUserList() {
         return R.ok().put("data", companyVoiceRoboticService.qwUserList());
     }
+
+    /**
+     * 启动任务(管理端适配:总后台无 LoginUser.company,通过 SecurityUtils 获取 tenantId)
+     */
+    @GetMapping("/taskRun")
+    public R taskRun(Long id) {
+        Long tenantId = SecurityUtils.getTenantId();
+        companyVoiceRoboticService.taskRun(id, tenantId);
+        return R.ok();
+    }
+
+    /**
+     * 查询任务执行记录
+     */
+    @GetMapping("/execRecords")
+    public R getExecRecords(@RequestParam Long roboticId,
+                            @RequestParam(defaultValue = "1") Integer pageNum,
+                            @RequestParam(defaultValue = "10") Integer pageSize,
+                            @RequestParam(required = false) String customerName,
+                            @RequestParam(required = false) String customerPhone,
+                            @RequestParam Boolean onlyCallNode) {
+        return R.ok(companyVoiceRoboticService.getExecRecords(roboticId, pageNum, pageSize,
+                customerName, customerPhone, onlyCallNode, null));
+    }
+
+    /**
+     * 查询外呼任务运行状态(管理端适配:通过 taskId 查 companyId 后调用 AiCallService)
+     */
+    @GetMapping("/statusList")
+    public R statusList(String ids) {
+        if (aiCallService == null) {
+            return R.error("管理后台暂不支持查询外呼状态");
+        }
+        List<String> taskIdList = Arrays.stream(ids.split(","))
+                .filter(s -> s != null && !s.isEmpty())
+                .collect(Collectors.toList());
+        Map<String, Object> resultMap = new HashMap<>();
+        for (String taskId : taskIdList) {
+            Long companyId = getCompanyIdByTaskId(taskId);
+            if (companyId != null) {
+                try {
+                    resultMap.put(taskId, aiCallService.queryCallTaskInfo(
+                            TaskInfo.builder().taskID(taskId).build(), companyId));
+                } catch (Exception ignored) {
+                }
+            }
+        }
+        return R.ok().put("data", resultMap);
+    }
+
+    /**
+     * 开启外呼任务(管理端适配)
+     */
+    @GetMapping("/startRobotic")
+    public R startRobotic(String taskId) {
+        if (aiCallService == null) {
+            return R.error("管理后台暂不支持开启外呼任务");
+        }
+        Long companyId = getCompanyIdByTaskId(taskId);
+        if (companyId == null) {
+            return R.error("未找到任务对应的公司");
+        }
+        aiCallService.startCallTask(TaskInfo.builder().taskID(taskId).build(), companyId);
+        return R.ok();
+    }
+
+    /**
+     * 停止外呼任务(管理端适配)
+     */
+    @GetMapping("/stopRobotic")
+    public R stopRobotic(String taskId) {
+        if (aiCallService == null) {
+            return R.error("管理后台暂不支持停止外呼任务");
+        }
+        Long companyId = getCompanyIdByTaskId(taskId);
+        if (companyId == null) {
+            return R.error("未找到任务对应的公司");
+        }
+        aiCallService.stopCallTask(TaskInfo.builder().taskID(taskId).build(), companyId);
+        return R.ok();
+    }
+
+    /**
+     * 通过 taskId 查询对应的 companyId
+     */
+    private Long getCompanyIdByTaskId(String taskId) {
+        return companyVoiceRoboticService.getCompanyIdByTaskId(taskId);
+    }
 }

+ 160 - 0
fs-admin-saas/src/main/java/com/fs/company/controller/CompanyWorkflowController.java

@@ -0,0 +1,160 @@
+package com.fs.company.controller;
+
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.company.domain.CompanyWorkflow;
+import com.fs.company.domain.CompanyWorkflowNodeType;
+import com.fs.company.param.CompanyWorkflowSaveParam;
+import com.fs.company.param.CompanyWorkflowUpdateBindWCParam;
+import com.fs.company.service.ICompanyWorkflowService;
+import com.fs.company.vo.OptionVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * AI工作流Controller(fs-admin-saas 桥接)
+ * <p>
+ * 原 CompanyWorkflowController 位于 com.fs.company.controller.company 子包下,
+ * 该子包在 fs-saasadmin 中被排除加载,本控制器在根包下提供相同的 API 端点。
+ * 总后台无 LoginUser.company 上下文,通过参数传递或 service 方法适配。
+ *
+ * @author fs
+ * @date 2026-01-06
+ */
+@RestController
+@RequestMapping("/company/companyWorkflow")
+public class CompanyWorkflowController extends BaseController {
+
+    @Autowired
+    private ICompanyWorkflowService companyWorkflowService;
+
+    @GetMapping("/list")
+    public TableDataInfo list(CompanyWorkflow fsAiWorkflow) {
+        startPage();
+        List<CompanyWorkflow> list = companyWorkflowService.selectCompanyWorkflowList(fsAiWorkflow);
+        return getDataTable(list);
+    }
+
+    @Log(title = "AI工作流", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(CompanyWorkflow fsAiWorkflow) {
+        List<CompanyWorkflow> list = companyWorkflowService.selectCompanyWorkflowList(fsAiWorkflow);
+        ExcelUtil<CompanyWorkflow> util = new ExcelUtil<CompanyWorkflow>(CompanyWorkflow.class);
+        return util.exportExcel(list, "AI工作流数据");
+    }
+
+    @GetMapping(value = "/{workflowId}")
+    public AjaxResult getInfo(@PathVariable("workflowId") Long workflowId) {
+        return AjaxResult.success(companyWorkflowService.selectCompanyWorkflowById(workflowId));
+    }
+
+    /**
+     * 保存AI工作流(管理端适配:companyId、companyUserId 从请求体获取)
+     */
+    @Log(title = "AI工作流", businessType = BusinessType.INSERT)
+    @PostMapping("/save")
+    public AjaxResult save(@RequestBody CompanyWorkflowSaveParam param) {
+        Long workflowId = companyWorkflowService.saveCompanyWorkflow(param);
+        return AjaxResult.success(workflowId);
+    }
+
+    @Log(title = "AI工作流", businessType = BusinessType.UPDATE)
+    @PutMapping("/status/{workflowId}/{status}")
+    public AjaxResult updateStatus(@PathVariable("workflowId") Long workflowId,
+                                   @PathVariable("status") Integer status) {
+        return toAjax(companyWorkflowService.updateCompanyWorkflowStatus(workflowId, status));
+    }
+
+    @Log(title = "AI工作流", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{workflowIds}")
+    public AjaxResult remove(@PathVariable Long[] workflowIds) {
+        return toAjax(companyWorkflowService.deleteCompanyWorkflowByIds(workflowIds));
+    }
+
+    @Log(title = "AI工作流", businessType = BusinessType.INSERT)
+    @PostMapping("/copy/{workflowId}")
+    public AjaxResult copy(@PathVariable("workflowId") Long workflowId) {
+        Long newWorkflowId = companyWorkflowService.copyCompanyWorkflow(workflowId);
+        if (newWorkflowId != null) {
+            return AjaxResult.success(newWorkflowId);
+        }
+        return AjaxResult.error("复制失败,工作流不存在");
+    }
+
+    @GetMapping("/nodeTypes")
+    public AjaxResult getNodeTypes() {
+        List<CompanyWorkflowNodeType> list = companyWorkflowService.selectAllEnabledNodeTypes();
+        return AjaxResult.success(list);
+    }
+
+    @GetMapping("/exportJson/{workflowId}")
+    public AjaxResult exportJson(@PathVariable("workflowId") Long workflowId) {
+        return AjaxResult.success(companyWorkflowService.exportWorkflowJson(workflowId));
+    }
+
+    @GetMapping("/listCompanyUser")
+    public AjaxResult listCompanyUser() {
+        return AjaxResult.success(companyWorkflowService.listCompanyUser());
+    }
+
+    @GetMapping("/getCompanyUserById/{companyUserId}")
+    public AjaxResult getCompanyUserById(@PathVariable("companyUserId") Long companyUserId) {
+        return AjaxResult.success(companyWorkflowService.getCompanyUserById(companyUserId));
+    }
+
+    @GetMapping("/getBindCompanyUserByWorkflowId/{workflowId}")
+    public AjaxResult getBindCompanyUserByWorkflowId(@PathVariable("workflowId") Long workflowId) {
+        return AjaxResult.success(companyWorkflowService.getBindCompanyUserByWorkflowId(workflowId));
+    }
+
+    @PostMapping("/updateWorkflowBindCompanyUser")
+    public AjaxResult updateWorkflowBindCompanyUser(@RequestBody CompanyWorkflowUpdateBindWCParam param) {
+        return companyWorkflowService.updateWorkflowBindCompanyUser(param);
+    }
+
+    /**
+     * 获取工作流下拉列表(管理端适配)
+     * 总后台无当前公司上下文,查询全部公司的工作流。
+     * 传入 companyId 参数时可过滤指定公司。
+     */
+    @GetMapping("/optionList")
+    public R optionList(@RequestParam(required = false) Long companyId) {
+        List<OptionVO> result;
+        if (companyId == null) {
+            result = companyWorkflowService.optionListAll();
+        } else {
+            result = companyWorkflowService.optionList(companyId);
+        }
+        return R.ok().put("data", result != null ? result : new ArrayList<>());
+    }
+
+    @GetMapping("/versionList/{workflowId}")
+    public AjaxResult versionList(@PathVariable Long workflowId) {
+        return AjaxResult.success(companyWorkflowService.selectVersionListByWorkflowId(workflowId));
+    }
+
+    @GetMapping("/versionDetail/{versionId}")
+    public AjaxResult versionDetail(@PathVariable Long versionId) {
+        return AjaxResult.success(companyWorkflowService.selectVersionDetailByVersionId(versionId));
+    }
+
+    @PostMapping("/versionRollback/{versionId}")
+    public AjaxResult rollbackVersion(@PathVariable Long versionId) {
+        Long workflowId = companyWorkflowService.rollbackWorkflowVersion(versionId, null);
+        return AjaxResult.success(workflowId);
+    }
+
+    @GetMapping("/nodeTypeCodes/{workflowId}")
+    public R getNodeTypeCodes(@PathVariable("workflowId") Long workflowId) {
+        List<String> typeCodes = companyWorkflowService.selectNodeTypeCodesByWorkflowId(workflowId);
+        return R.ok().put("data", typeCodes);
+    }
+}

+ 2 - 0
fs-service/src/main/java/com/fs/company/mapper/CompanyWorkflowMapper.java

@@ -57,4 +57,6 @@ public interface CompanyWorkflowMapper extends BaseMapper<CompanyWorkflow>  {
     List<CompanyWorkflowNodeVoiceVo> getMyWorkflowNodes(@Param("companyUserId") Long companyUserId);
 
     List<OptionVO> optionList(@Param("companyId") Long companyId);
+
+    List<OptionVO> optionListAll();
 }

+ 5 - 1
fs-service/src/main/java/com/fs/company/service/ICompanyVoiceRoboticService.java

@@ -127,5 +127,9 @@ public interface ICompanyVoiceRoboticService extends IService<CompanyVoiceRoboti
      * @return 追加结果(成功数、重复客户信息)
      */
     R appendCustomersToRunningTask(Long taskId, List<Long> customerIds);
-    
+
+    /**
+     * 通过三方任务ID查询公司ID(管理端适配)
+     */
+    Long getCompanyIdByTaskId(String taskId);
 }

+ 5 - 0
fs-service/src/main/java/com/fs/company/service/ICompanyWorkflowService.java

@@ -108,5 +108,10 @@ public interface ICompanyWorkflowService {
 
     List<OptionVO> optionList(Long companyId);
 
+    /**
+     * 获取全部工作流下拉列表(管理端适配,不限公司)
+     */
+    List<OptionVO> optionListAll();
+
     List<String> selectNodeTypeCodesByWorkflowId(Long workflowId);
 }

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

@@ -2416,4 +2416,11 @@ public class CompanyVoiceRoboticServiceImpl extends ServiceImpl<CompanyVoiceRobo
             log.error("重建CONTINUE:TIMER:EXECUTE Redis key异常, instanceId={}", exec.getWorkflowInstanceId(), e);
         }
     }
+
+    @Override
+    public Long getCompanyIdByTaskId(String taskId) {
+        CompanyVoiceRobotic robotic = companyVoiceRoboticMapper.selectOne(
+                new LambdaQueryWrapper<CompanyVoiceRobotic>().eq(CompanyVoiceRobotic::getTaskId, Long.valueOf(taskId)));
+        return robotic != null ? robotic.getCompanyId() : null;
+    }
 }

+ 5 - 0
fs-service/src/main/java/com/fs/company/service/impl/CompanyWorkflowServiceImpl.java

@@ -467,6 +467,11 @@ public class CompanyWorkflowServiceImpl implements ICompanyWorkflowService {
         return companyWorkflowMapper.optionList(companyId);
     }
 
+    @Override
+    public List<OptionVO> optionListAll() {
+        return companyWorkflowMapper.optionListAll();
+    }
+
 
     @Override
     public List<CompanyWorkflowVersionVo> selectVersionListByWorkflowId(Long workflowId) {

+ 4 - 0
fs-service/src/main/resources/mapper/company/CompanyWorkflowMapper.xml

@@ -117,6 +117,10 @@
         select workflow_id value,workflow_name label from company_ai_workflow where company_id = #{companyId} and del_flag = 0
     </select>
 
+    <select id="optionListAll" resultType="com.fs.company.vo.OptionVO">
+        select workflow_id value,workflow_name label from company_ai_workflow where del_flag = 0 order by workflow_id
+    </select>
+
     <insert id="insertCompanyWorkflow" useGeneratedKeys="true" keyProperty="workflowId">
         insert into company_ai_workflow
         <trim prefix="(" suffix=")" suffixOverrides=",">