浏览代码

大模型版本管理代码提交

yjwang 1 天之前
父节点
当前提交
8a782f8119
共有 44 个文件被更改,包括 4754 次插入1 次删除
  1. 328 0
      fs-company/src/main/java/com/fs/company/controller/aicall/CcLlmAgentAccountController.java
  2. 134 0
      fs-company/src/main/java/com/fs/company/controller/aicall/CcLlmAgentProviderController.java
  3. 153 0
      fs-company/src/main/java/com/fs/company/controller/aicall/CcLlmKbCatController.java
  4. 22 1
      fs-company/src/main/java/com/fs/company/controller/company/EasyCallController.java
  5. 162 0
      fs-service/src/main/java/com/fs/aicall/domain/CcCallTask.java
  6. 68 0
      fs-service/src/main/java/com/fs/aicall/domain/CcLlmAgentAccount.java
  7. 29 0
      fs-service/src/main/java/com/fs/aicall/domain/CcLlmAgentProvider.java
  8. 31 0
      fs-service/src/main/java/com/fs/aicall/domain/CcLlmKb.java
  9. 31 0
      fs-service/src/main/java/com/fs/aicall/domain/CcLlmKbCat.java
  10. 45 0
      fs-service/src/main/java/com/fs/aicall/domain/CcParams.java
  11. 38 0
      fs-service/src/main/java/com/fs/aicall/domain/CompanyBindAiModel.java
  12. 82 0
      fs-service/src/main/java/com/fs/aicall/mapper/CcCallTaskMapper.java
  13. 70 0
      fs-service/src/main/java/com/fs/aicall/mapper/CcLlmAgentAccountMapper.java
  14. 71 0
      fs-service/src/main/java/com/fs/aicall/mapper/CcLlmAgentProviderMapper.java
  15. 70 0
      fs-service/src/main/java/com/fs/aicall/mapper/CcLlmKbCatMapper.java
  16. 79 0
      fs-service/src/main/java/com/fs/aicall/mapper/CcLlmKbMapper.java
  17. 79 0
      fs-service/src/main/java/com/fs/aicall/mapper/CcParamsMapper.java
  18. 90 0
      fs-service/src/main/java/com/fs/aicall/mapper/CompanyBindAiModelMapper.java
  19. 71 0
      fs-service/src/main/java/com/fs/aicall/service/ICcCallTaskService.java
  20. 62 0
      fs-service/src/main/java/com/fs/aicall/service/ICcLlmAgentAccountService.java
  21. 63 0
      fs-service/src/main/java/com/fs/aicall/service/ICcLlmAgentProviderService.java
  22. 71 0
      fs-service/src/main/java/com/fs/aicall/service/ICcLlmKbCatService.java
  23. 70 0
      fs-service/src/main/java/com/fs/aicall/service/ICcLlmKbService.java
  24. 83 0
      fs-service/src/main/java/com/fs/aicall/service/ICcParamsService.java
  25. 105 0
      fs-service/src/main/java/com/fs/aicall/service/ICompanyBindAiModelService.java
  26. 118 0
      fs-service/src/main/java/com/fs/aicall/service/impl/CcCallTaskServiceImpl.java
  27. 95 0
      fs-service/src/main/java/com/fs/aicall/service/impl/CcLlmAgentAccountServiceImpl.java
  28. 95 0
      fs-service/src/main/java/com/fs/aicall/service/impl/CcLlmAgentProviderServiceImpl.java
  29. 113 0
      fs-service/src/main/java/com/fs/aicall/service/impl/CcLlmKbCatServiceImpl.java
  30. 154 0
      fs-service/src/main/java/com/fs/aicall/service/impl/CcLlmKbServiceImpl.java
  31. 168 0
      fs-service/src/main/java/com/fs/aicall/service/impl/CcParamsServiceImpl.java
  32. 170 0
      fs-service/src/main/java/com/fs/aicall/service/impl/CompanyBindAiModelServiceImpl.java
  33. 105 0
      fs-service/src/main/java/com/fs/aicall/utils/CommonUtils.java
  34. 672 0
      fs-service/src/main/java/com/fs/aicall/utils/StringUtils.java
  35. 12 0
      fs-service/src/main/java/com/fs/aicall/utils/XSSFUtils.java
  36. 276 0
      fs-service/src/main/java/com/fs/aicall/utils/http/HttpUtils.java
  37. 9 0
      fs-service/src/main/resources/application-dev.yml
  38. 178 0
      fs-service/src/main/resources/mapper/aicall/CcCallTaskMapper.xml
  39. 106 0
      fs-service/src/main/resources/mapper/aicall/CcLlmAgentAccountMapper.xml
  40. 62 0
      fs-service/src/main/resources/mapper/aicall/CcLlmAgentProviderMapper.xml
  41. 62 0
      fs-service/src/main/resources/mapper/aicall/CcLlmKbCatMapper.xml
  42. 90 0
      fs-service/src/main/resources/mapper/aicall/CcLlmKbMapper.xml
  43. 82 0
      fs-service/src/main/resources/mapper/aicall/CcParamsMapper.xml
  44. 80 0
      fs-service/src/main/resources/mapper/aicall/CompanyBindAiModelMapper.xml

+ 328 - 0
fs-company/src/main/java/com/fs/company/controller/aicall/CcLlmAgentAccountController.java

@@ -0,0 +1,328 @@
+package com.fs.company.controller.aicall;
+
+import com.alibaba.fastjson.JSONObject;
+import com.fs.aicall.domain.CcCallTask;
+import com.fs.aicall.domain.CcLlmAgentAccount;
+import com.fs.aicall.service.ICcLlmAgentAccountService;
+import com.fs.aicall.service.ICcParamsService;
+import com.fs.aicall.service.ICcCallTaskService;
+import com.fs.aicall.service.ICompanyBindAiModelService;
+import com.fs.aicall.utils.CommonUtils;
+import com.fs.aicall.utils.StringUtils;
+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.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.ServletUtils;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.framework.security.LoginUser;
+import com.fs.framework.service.TokenService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.ModelMap;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * 机器人参数配置Controller
+ * 
+ * @author ruoyi
+ * @date 2025-06-16
+ */
+@Controller
+@RequestMapping("/aicall/account")
+public class CcLlmAgentAccountController extends BaseController
+{
+    private String prefix = "aicall/account";
+
+    @Autowired
+    private ICcLlmAgentAccountService ccLlmAgentAccountService;
+    @Autowired
+    private ICcParamsService ccParamsService;
+    @Autowired
+    private ICcCallTaskService ccCallTaskService;
+    @Autowired
+    private ICompanyBindAiModelService companyBindAiModelService;
+
+    @Autowired
+    private TokenService tokenService;
+
+    private static List<String> hideKeys = Arrays.asList("apiKey", "oauthPrivateKey", "oauthPublicKeyId", "patToken");
+
+
+    @PreAuthorize("@ss.hasPermi('aicall:account:view')")
+    @GetMapping()
+    public String account()
+    {
+        return prefix + "/account";
+    }
+
+    /**
+     * 查询机器人参数配置列表
+     */
+    @PreAuthorize("@ss.hasPermi('aicall:account:list')")
+    @PostMapping("/list")
+    @ResponseBody
+    public TableDataInfo list(@RequestBody CcLlmAgentAccount ccLlmAgentAccount)
+    {
+        // 获取当前登录的公司ID
+        Long companyId = getCurrentCompanyId();
+        if (companyId != null) {
+            List<Long> modelIds = companyBindAiModelService.selectModelIdsByCompanyId(companyId);
+            if (!modelIds.isEmpty()) {
+                ccLlmAgentAccount.setModelIds(modelIds);
+            } else {
+                return getDataTable(new ArrayList<>());
+            }
+        }
+        
+        startPage();
+        List<CcLlmAgentAccount> list = ccLlmAgentAccountService.selectCcLlmAgentAccountList(ccLlmAgentAccount);
+        TableDataInfo tableDataInfo = getDataTable(list);
+        List<CcLlmAgentAccount> records = (List<CcLlmAgentAccount>) tableDataInfo.getRows();
+        for (CcLlmAgentAccount data: records) {
+            JSONObject accountJson = JSONObject.parseObject(data.getAccountJson());
+            for (String key: accountJson.keySet()) {
+                if (hideKeys.contains(key)) {
+                    accountJson.put(key, CommonUtils.maskStringUtil(accountJson.getString(key)));
+                }
+            }
+            data.setAccountJson(JSONObject.toJSONString(accountJson));
+        }
+        tableDataInfo.setRows(records);
+        return tableDataInfo;
+    }
+
+    /**
+     * 获取当前登录的公司ID
+     * @return 公司ID
+     */
+    private Long getCurrentCompanyId() {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        return loginUser.getUser().getCompanyId();
+    }
+
+    /**
+     * 导出机器人参数配置列表
+     */
+    @PreAuthorize("@ss.hasPermi('aicall:account:export')")
+    @Log(title = "机器人参数配置", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    @ResponseBody
+    public AjaxResult export(CcLlmAgentAccount ccLlmAgentAccount)
+    {
+        List<CcLlmAgentAccount> list = ccLlmAgentAccountService.selectCcLlmAgentAccountList(ccLlmAgentAccount);
+        ExcelUtil<CcLlmAgentAccount> util = new ExcelUtil<CcLlmAgentAccount>(CcLlmAgentAccount.class);
+        return util.exportExcel(list, "机器人参数配置数据");
+    }
+
+    /**
+     * 新增机器人参数配置
+     */
+    @GetMapping("/add")
+    public String add(ModelMap mmap)
+    {
+        CcLlmAgentAccount ccLlmAgentAccount = new CcLlmAgentAccount();
+        ccLlmAgentAccount.setInterruptIgnoreKeywords(ccParamsService.getParamValueByCode("default_interrupt_ignore_keywords", ""));
+        ccLlmAgentAccount.setInterruptFlag(0);
+        ccLlmAgentAccount.setAccountJson("{}");
+        mmap.put("ccLlmAgentAccount", ccLlmAgentAccount);
+        return prefix + "/add";
+    }
+
+    /**
+     * 新增保存机器人参数配置
+     */
+    @PreAuthorize("@ss.hasPermi('aicall:account:add')")
+    @Log(title = "机器人参数配置", businessType = BusinessType.INSERT)
+    @PostMapping("/add")
+    @ResponseBody
+    public AjaxResult addSave(@RequestBody CcLlmAgentAccount ccLlmAgentAccount)
+    {
+        if ("Coze".equalsIgnoreCase(ccLlmAgentAccount.getProviderClassName())) {
+            ccLlmAgentAccount.setAccountEntity("CozeAccount");
+        } else if ("LocalNlpChat".equalsIgnoreCase(ccLlmAgentAccount.getProviderClassName())) {
+            ccLlmAgentAccount.setAccountEntity("CozeAccount");
+        } else if ("JiutianWorkflow".equalsIgnoreCase(ccLlmAgentAccount.getProviderClassName())
+                || "JiutianAgent".equalsIgnoreCase(ccLlmAgentAccount.getProviderClassName())) {
+            ccLlmAgentAccount.setAccountEntity("JiutianAccount");
+        } else {
+            ccLlmAgentAccount.setAccountEntity("LlmAccount");
+        }
+        if (null == ccLlmAgentAccount.getKbCatId()) {
+            ccLlmAgentAccount.setKbCatId(-1);
+        }
+        
+        // 新增模型
+        int result = ccLlmAgentAccountService.insertCcLlmAgentAccount(ccLlmAgentAccount);
+        
+        // 获取当前登录的公司ID
+        Long companyId = getCurrentCompanyId();
+        if (result > 0 && companyId != null) {
+            companyBindAiModelService.bindCompanyToModel(ccLlmAgentAccount.getId().longValue(), companyId);
+        }
+        
+        return toAjax(result);
+    }
+
+    /**
+     * 修改机器人参数配置
+     */
+    @PreAuthorize("@ss.hasPermi('aicall:account:edit')")
+    @GetMapping("/edit/{id}")
+    public String edit(@PathVariable("id") Integer id, ModelMap mmap)
+    {
+        CcLlmAgentAccount ccLlmAgentAccount = ccLlmAgentAccountService.selectCcLlmAgentAccountById(id);
+        if (StringUtils.isBlank(ccLlmAgentAccount.getInterruptIgnoreKeywords())) {
+            ccLlmAgentAccount.setInterruptIgnoreKeywords(ccParamsService.getParamValueByCode("default_interrupt_ignore_keywords", ""));
+        }
+        JSONObject accountJson = JSONObject.parseObject(ccLlmAgentAccount.getAccountJson());
+        for (String key: accountJson.keySet()) {
+            if (hideKeys.contains(key)) {
+                accountJson.put(key, CommonUtils.maskStringUtil(accountJson.getString(key)));
+            }
+        }
+        ccLlmAgentAccount.setAccountJson(JSONObject.toJSONString(accountJson));
+        mmap.put("ccLlmAgentAccount", ccLlmAgentAccount);
+
+        String errMsg = checkEdit(ccLlmAgentAccount.getId());
+        mmap.put("errorMsg", errMsg);
+        return prefix + "/edit";
+    }
+
+    /**
+     * 复制机器人参数配置
+     */
+    @PreAuthorize("@ss.hasPermi('aicall:account:edit')")
+    @GetMapping("/copy/{id}")
+    public String copy(@PathVariable("id") Integer id, ModelMap mmap)
+    {
+        CcLlmAgentAccount ccLlmAgentAccount = ccLlmAgentAccountService.selectCcLlmAgentAccountById(id);
+        if (StringUtils.isBlank(ccLlmAgentAccount.getInterruptIgnoreKeywords())) {
+            ccLlmAgentAccount.setInterruptIgnoreKeywords(ccParamsService.getParamValueByCode("default_interrupt_ignore_keywords", ""));
+        }
+        ccLlmAgentAccount.setId(-1*ccLlmAgentAccount.getId());
+        ccLlmAgentAccount.setName(ccLlmAgentAccount.getName() + "-副本");
+        JSONObject accountJson = JSONObject.parseObject(ccLlmAgentAccount.getAccountJson());
+        for (String key: accountJson.keySet()) {
+            if (hideKeys.contains(key)) {
+                accountJson.put(key, CommonUtils.maskStringUtil(accountJson.getString(key)));
+            }
+        }
+        ccLlmAgentAccount.setAccountJson(JSONObject.toJSONString(accountJson));
+        mmap.put("ccLlmAgentAccount", ccLlmAgentAccount);
+        return prefix + "/edit";
+    }
+
+    /**
+     * 修改保存机器人参数配置
+     */
+    @PreAuthorize("@ss.hasPermi('aicall:account:edit')")
+    @Log(title = "机器人参数配置", businessType = BusinessType.UPDATE)
+    @PostMapping("/edit")
+    @ResponseBody
+    public AjaxResult editSave(@RequestBody CcLlmAgentAccount ccLlmAgentAccount)
+    {
+
+        if (ccLlmAgentAccount.getId() > 0) {
+            String errMsg = checkEdit(ccLlmAgentAccount.getId());
+            if (StringUtils.isNotEmpty(errMsg)) {
+                return AjaxResult.error(errMsg);
+            }
+        }
+
+        if ("Coze".equalsIgnoreCase(ccLlmAgentAccount.getProviderClassName())) {
+            ccLlmAgentAccount.setAccountEntity("CozeAccount");
+        } else if ("LocalNlpChat".equalsIgnoreCase(ccLlmAgentAccount.getProviderClassName())) {
+            ccLlmAgentAccount.setAccountEntity("CozeAccount");
+        } else if ("JiutianWorkflow".equalsIgnoreCase(ccLlmAgentAccount.getProviderClassName())
+                || "JiutianAgent".equalsIgnoreCase(ccLlmAgentAccount.getProviderClassName())) {
+            ccLlmAgentAccount.setAccountEntity("JiutianAccount");
+        } else {
+            ccLlmAgentAccount.setAccountEntity("LlmAccount");
+        }
+
+        Integer orignId = ccLlmAgentAccount.getId();
+        if (orignId < 0) {
+            orignId = orignId * -1;
+        }
+        CcLlmAgentAccount oldCcLlmAgentAccount = ccLlmAgentAccountService.selectCcLlmAgentAccountById(orignId);
+        JSONObject oldAccountJson = JSONObject.parseObject(oldCcLlmAgentAccount.getAccountJson());
+        JSONObject newAccountJson = JSONObject.parseObject(ccLlmAgentAccount.getAccountJson());
+        for (String key: newAccountJson.keySet()) {
+            // 是需要脱敏的key,且值包含星号,则值不更新
+            if (hideKeys.contains(key) && newAccountJson.getString(key).contains("****")) {
+                newAccountJson.put(key, oldAccountJson.getString(key));
+            }
+        }
+        ccLlmAgentAccount.setAccountJson(JSONObject.toJSONString(newAccountJson));
+        if (null == ccLlmAgentAccount.getKbCatId()) {
+            ccLlmAgentAccount.setKbCatId(-1);
+        }
+
+        if (ccLlmAgentAccount.getId() > 0) {
+            return toAjax(ccLlmAgentAccountService.updateCcLlmAgentAccount(ccLlmAgentAccount));
+        } else {
+            ccLlmAgentAccount.setId(null);
+            return toAjax(ccLlmAgentAccountService.insertCcLlmAgentAccount(ccLlmAgentAccount));
+        }
+    }
+
+    /**
+     * 删除机器人参数配置
+     */
+    @PreAuthorize("@ss.hasPermi('aicall:account:remove')")
+    @Log(title = "机器人参数配置", businessType = BusinessType.DELETE)
+    @PostMapping( "/remove")
+    @ResponseBody
+    public AjaxResult remove(String ids)
+    {
+        return toAjax(ccLlmAgentAccountService.deleteCcLlmAgentAccountByIds(ids));
+    }
+
+
+    @GetMapping("/all")
+    @ResponseBody
+    public AjaxResult all()
+    {
+        List<CcLlmAgentAccount> list = ccLlmAgentAccountService.selectCcLlmAgentAccountList(new CcLlmAgentAccount());
+        for (CcLlmAgentAccount data: list) {
+            JSONObject accountJson = JSONObject.parseObject(data.getAccountJson());
+            for (String key: accountJson.keySet()) {
+                if (hideKeys.contains(key)) {
+                    accountJson.put(key, CommonUtils.maskStringUtil(accountJson.getString(key)));
+                }
+            }
+            data.setAccountJson(JSONObject.toJSONString(accountJson));
+        }
+        return AjaxResult.success(list);
+    }
+
+
+
+    private String checkEdit(Integer id)
+    {
+        CcLlmAgentAccount llmAgentAccount = ccLlmAgentAccountService.selectCcLlmAgentAccountById(id);
+        List<CcCallTask> ccCallTaskList = ccCallTaskService.selectCcCallTaskList(new CcCallTask().setLlmAccountId(id));
+        List<String> ids = new ArrayList<>();
+        for (CcCallTask ccCallTask: ccCallTaskList) {
+            // AI外呼,且不自动停止,且状态为正在拨打的任务,需要手动停止任务后再修改大模型配置再手动启动任务
+            if (ccCallTask.getTaskType() == 1
+                    && ccCallTask.getIfcall() == 1
+                    && ccCallTask.getAutoStop() == 0) {
+                ids.add(ccCallTask.getBatchName());
+            }
+        }
+        if (ids.size() > 0) {
+            return String.format("%s%s%s", "请先暂停任务:", StringUtils.join(ids, ","), ",再修改该配置,修改完成后再启动任务");
+        } else {
+            return "";
+        }
+    }
+
+}

+ 134 - 0
fs-company/src/main/java/com/fs/company/controller/aicall/CcLlmAgentProviderController.java

@@ -0,0 +1,134 @@
+package com.fs.company.controller.aicall;
+
+
+import com.fs.aicall.domain.CcLlmAgentProvider;
+import com.fs.aicall.service.ICcLlmAgentProviderService;
+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.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.poi.ExcelUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.ModelMap;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 大模型实现类列表Controller
+ * 
+ * @author ruoyi
+ * @date 2025-06-16
+ */
+@Controller
+@RequestMapping("/aicall/provider")
+public class CcLlmAgentProviderController extends BaseController
+{
+    private String prefix = "aicall/provider";
+
+    @Autowired
+    private ICcLlmAgentProviderService ccLlmAgentProviderService;
+
+    @PreAuthorize("@ss.hasPermi('aicall:provider:view')")
+    @GetMapping()
+    public String provider()
+    {
+        return prefix + "/provider";
+    }
+
+    /**
+     * 查询大模型实现类列表列表
+     */
+    @PreAuthorize("@ss.hasPermi('aicall:provider:list')")
+    @PostMapping("/list")
+    @ResponseBody
+    public TableDataInfo list(CcLlmAgentProvider ccLlmAgentProvider)
+    {
+        startPage();
+        List<CcLlmAgentProvider> list = ccLlmAgentProviderService.selectCcLlmAgentProviderList(ccLlmAgentProvider);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出大模型实现类列表列表
+     */
+    @PreAuthorize("@ss.hasPermi('aicall:provider:export')")
+    @Log(title = "大模型实现类列表", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    @ResponseBody
+    public AjaxResult export(CcLlmAgentProvider ccLlmAgentProvider)
+    {
+        List<CcLlmAgentProvider> list = ccLlmAgentProviderService.selectCcLlmAgentProviderList(ccLlmAgentProvider);
+        ExcelUtil<CcLlmAgentProvider> util = new ExcelUtil<CcLlmAgentProvider>(CcLlmAgentProvider.class);
+        return util.exportExcel(list, "大模型实现类列表数据");
+    }
+
+    /**
+     * 新增大模型实现类列表
+     */
+    @GetMapping("/add")
+    public String add()
+    {
+        return prefix + "/add";
+    }
+
+    /**
+     * 新增保存大模型实现类列表
+     */
+    @PreAuthorize("@ss.hasPermi('aicall:provider:add')")
+    @Log(title = "大模型实现类列表", businessType = BusinessType.INSERT)
+    @PostMapping("/add")
+    @ResponseBody
+    public AjaxResult addSave(CcLlmAgentProvider ccLlmAgentProvider)
+    {
+        return toAjax(ccLlmAgentProviderService.insertCcLlmAgentProvider(ccLlmAgentProvider));
+    }
+
+    /**
+     * 修改大模型实现类列表
+     */
+    @PreAuthorize("@ss.hasPermi('aicall:provider:edit')")
+    @GetMapping("/edit/{id}")
+    public String edit(@PathVariable("id") Integer id, ModelMap mmap)
+    {
+        CcLlmAgentProvider ccLlmAgentProvider = ccLlmAgentProviderService.selectCcLlmAgentProviderById(id);
+        mmap.put("ccLlmAgentProvider", ccLlmAgentProvider);
+        return prefix + "/edit";
+    }
+
+    /**
+     * 修改保存大模型实现类列表
+     */
+    @PreAuthorize("@ss.hasPermi('aicall:provider:edit')")
+    @Log(title = "大模型实现类列表", businessType = BusinessType.UPDATE)
+    @PostMapping("/edit")
+    @ResponseBody
+    public AjaxResult editSave(CcLlmAgentProvider ccLlmAgentProvider)
+    {
+        return toAjax(ccLlmAgentProviderService.updateCcLlmAgentProvider(ccLlmAgentProvider));
+    }
+
+    /**
+     * 删除大模型实现类列表
+     */
+    @PreAuthorize("@ss.hasPermi('aicall:provider:remove')")
+    @Log(title = "大模型实现类列表", businessType = BusinessType.DELETE)
+    @PostMapping( "/remove")
+    @ResponseBody
+    public AjaxResult remove(String ids)
+    {
+        return toAjax(ccLlmAgentProviderService.deleteCcLlmAgentProviderByIds(ids));
+    }
+
+    @GetMapping("/all")
+    @ResponseBody
+    public AjaxResult all()
+    {
+        List<CcLlmAgentProvider> list = ccLlmAgentProviderService.selectCcLlmAgentProviderList(new CcLlmAgentProvider());
+        return AjaxResult.success(list);
+    }
+
+}

+ 153 - 0
fs-company/src/main/java/com/fs/company/controller/aicall/CcLlmKbCatController.java

@@ -0,0 +1,153 @@
+package com.fs.company.controller.aicall;
+
+import com.fs.aicall.domain.CcLlmKbCat;
+import com.fs.aicall.service.ICcLlmKbCatService;
+import com.fs.aicall.service.ICcLlmKbService;
+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.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.poi.ExcelUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.ModelMap;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 知识库Controller
+ * 
+ * @author ruoyi
+ * @date 2026-01-19
+ */
+@Controller
+@RequestMapping("/aicall/kbcat")
+public class CcLlmKbCatController extends BaseController
+{
+    private String prefix = "aicall/kbcat";
+
+    @Autowired
+    private ICcLlmKbCatService ccLlmKbCatService;
+    @Autowired
+    private ICcLlmKbService ccLlmKbService;
+
+    @PreAuthorize("@ss.hasPermi('aicall:kbcat:view')")
+    @GetMapping()
+    public String kbcat()
+    {
+        return prefix + "/kbcat";
+    }
+
+    /**
+     * 查询知识库列表
+     */
+    @PreAuthorize("@ss.hasPermi('aicall:kbcat:list')")
+    @PostMapping("/list")
+    @ResponseBody
+    public TableDataInfo list(CcLlmKbCat ccLlmKbCat)
+    {
+        startPage();
+        List<CcLlmKbCat> list = ccLlmKbCatService.selectCcLlmKbCatList(ccLlmKbCat);
+        for (CcLlmKbCat data: list) {
+            data.setContentCount(ccLlmKbService.selectCountByCatId(data.getId()));
+        }
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出知识库列表
+     */
+    @PreAuthorize("@ss.hasPermi('aicall:kbcat:export')")
+    @Log(title = "知识库", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    @ResponseBody
+    public AjaxResult export(CcLlmKbCat ccLlmKbCat)
+    {
+        List<CcLlmKbCat> list = ccLlmKbCatService.selectCcLlmKbCatList(ccLlmKbCat);
+        ExcelUtil<CcLlmKbCat> util = new ExcelUtil<CcLlmKbCat>(CcLlmKbCat.class);
+        return util.exportExcel(list, "知识库数据");
+    }
+
+    /**
+     * 新增知识库
+     */
+    @GetMapping("/add")
+    public String add(ModelMap mmap)
+    {
+        mmap.put("ccLlmKbCat", new CcLlmKbCat());
+        return prefix + "/add";
+    }
+
+    /**
+     * 新增保存知识库
+     */
+    @PreAuthorize("@ss.hasPermi('aicall:kbcat:add')")
+    @Log(title = "知识库", businessType = BusinessType.INSERT)
+    @PostMapping("/add")
+    @ResponseBody
+    public AjaxResult addSave(CcLlmKbCat ccLlmKbCat)
+    {
+        CcLlmKbCat checkCcLlmKbCat = ccLlmKbCatService.selectCcLlmKbCatByCat(null, ccLlmKbCat.getCat());
+        if (null != checkCcLlmKbCat) {
+            return AjaxResult.error("知识库分类不能重复,请修改");
+        }
+        return toAjax(ccLlmKbCatService.insertCcLlmKbCat(ccLlmKbCat));
+    }
+
+    /**
+     * 修改知识库
+     */
+    @PreAuthorize("@ss.hasPermi('aicall:kbcat:edit')")
+    @GetMapping("/edit/{id}")
+    public String edit(@PathVariable("id") Long id, ModelMap mmap)
+    {
+        CcLlmKbCat ccLlmKbCat = ccLlmKbCatService.selectCcLlmKbCatById(id);
+        mmap.put("ccLlmKbCat", ccLlmKbCat);
+        return prefix + "/edit";
+    }
+
+    /**
+     * 修改保存知识库
+     */
+    @PreAuthorize("@ss.hasPermi('aicall:kbcat:edit')")
+    @Log(title = "知识库", businessType = BusinessType.UPDATE)
+    @PostMapping("/edit")
+    @ResponseBody
+    public AjaxResult editSave(CcLlmKbCat ccLlmKbCat)
+    {
+        CcLlmKbCat checkCcLlmKbCat = ccLlmKbCatService.selectCcLlmKbCatByCat(ccLlmKbCat.getId(), ccLlmKbCat.getCat());
+        if (null != checkCcLlmKbCat) {
+            return AjaxResult.error("知识库分类不能重复,请修改");
+        }
+        return toAjax(ccLlmKbCatService.updateCcLlmKbCat(ccLlmKbCat));
+    }
+
+    /**
+     * 删除知识库
+     */
+    @PreAuthorize("@ss.hasPermi('aicall:kbcat:remove')")
+    @Log(title = "知识库", businessType = BusinessType.DELETE)
+    @PostMapping( "/remove")
+    @ResponseBody
+    public AjaxResult remove(String ids)
+    {
+        return toAjax(ccLlmKbCatService.deleteCcLlmKbCatByIds(ids));
+    }
+
+
+
+
+    @GetMapping("/all")
+    @ResponseBody
+    public AjaxResult all()
+    {
+        List<CcLlmKbCat> list = ccLlmKbCatService.selectCcLlmKbCatList(new CcLlmKbCat());
+        return AjaxResult.success(list);
+    }
+
+
+
+}

+ 22 - 1
fs-company/src/main/java/com/fs/company/controller/company/EasyCallController.java

@@ -1,5 +1,8 @@
 package com.fs.company.controller.company;
 
+import com.fs.aicall.domain.CcLlmAgentAccount;
+import com.fs.aicall.service.ICcLlmAgentAccountService;
+import com.fs.aicall.service.ICompanyBindAiModelService;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.R;
 import com.fs.common.utils.ServletUtils;
@@ -12,6 +15,8 @@ import io.swagger.annotations.ApiOperation;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
+import java.util.ArrayList;
+import java.util.LinkedList;
 import java.util.List;
 
 /**
@@ -35,6 +40,12 @@ public class EasyCallController extends BaseController {
     @Autowired
     private TokenService tokenService;
 
+    @Autowired
+    private ICompanyBindAiModelService companyBindAiModelService;
+
+    @Autowired
+    private ICcLlmAgentAccountService ccLlmAgentAccountService;
+
     // =================== 基础数据查询 ===================
 
     /**
@@ -60,7 +71,17 @@ public class EasyCallController extends BaseController {
     public R getLlmAccountList() {
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         Long companyId = loginUser.getUser().getCompanyId();
-        List<EasyCallLlmAccountVO> list = easyCallService.getLlmAccountList(companyId);
+        CcLlmAgentAccount ccLlmAgentAccount = new CcLlmAgentAccount();
+        if (companyId != null) {
+            List<Long> modelIds = companyBindAiModelService.selectModelIdsByCompanyId(companyId);
+            if (!modelIds.isEmpty()) {
+                ccLlmAgentAccount.setModelIds(modelIds);
+            } else {
+                return R.ok().put("data", new ArrayList<>());
+            }
+        }
+        List<CcLlmAgentAccount> list = ccLlmAgentAccountService.selectCcLlmAgentAccountList(ccLlmAgentAccount);
+//        List<EasyCallLlmAccountVO> list = easyCallService.getLlmAccountList(companyId);
         return R.ok().put("data", list);
     }
 

+ 162 - 0
fs-service/src/main/java/com/fs/aicall/domain/CcCallTask.java

@@ -0,0 +1,162 @@
+package com.fs.aicall.domain;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 外呼任务对象 cc_call_task
+ * 
+ * @author ruoyi
+ * @date 2025-05-29
+ */
+@Data
+@Accessors(chain = true)
+public class CcCallTask implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**  */
+    private Long batchId;
+
+    /** 外呼任务的业务组 */
+    @Excel(name = "外呼任务的业务组")
+    private String groupId;
+
+    /**  */
+    @Excel(name = "")
+    private String batchName;
+
+    /** 是否启动任务, 1 启动, 0 停止 */
+    @Excel(name = "是否启动任务, 1 启动, 0 停止")
+    private Integer ifcall;
+
+    /** 外呼速率 */
+    @Excel(name = "外呼速率")
+    private Double rate;
+
+    /** 当前任务最大可用外线数 */
+    @Excel(name = "当前任务最大可用外线数")
+    private Long threadNum;
+
+    /** 创建时间 */
+    @Excel(name = "创建时间")
+    private Long createtime;
+
+    /** 任务是否正在执行; */
+    @Excel(name = "任务是否正在执行;")
+    private Long executing;
+
+    /** 任务停止时间 */
+    @Excel(name = "任务停止时间")
+    private Long stopTime;
+
+    /** 任务创建者用户id */
+    @Excel(name = "任务创建者用户id")
+    private String userid;
+
+    /** 0 Pure manual outbound call; 1 AI outbound calling; 2 voice call notification. */
+    @Excel(name = "0 Pure manual outbound call; 1 AI outbound calling; 2 voice call notification.")
+    private Integer taskType;
+
+    /** 使用哪条线路外呼 */
+    @Excel(name = "使用哪条线路外呼")
+    private Long gatewayId;
+
+    /** 音色 */
+    @Excel(name = "音色")
+    private String voiceCode;
+
+    /** 音源 */
+    @Excel(name = "音源")
+    private String voiceSource;
+
+    /** The average ringing duration of the call; seconds */
+    @Excel(name = "The average ringing duration of the call; seconds")
+    private Double avgRingTimeLen;
+
+    /** The average pure call duration per call; seconds */
+    @Excel(name = "The average pure call duration per call; seconds")
+    private Double avgCallTalkTimeLen;
+
+    /** The duration of form filling after the call ends; seconds */
+    @Excel(name = "The duration of form filling after the call ends; seconds")
+    private Double avgCallEndProcessTimeLen;
+
+    /** 外呼节点 */
+    @Excel(name = "外呼节点")
+    private String callNodeNo;
+
+    /** 大模型底座账号的Id */
+    @Excel(name = "大模型底座账号的Id")
+    private Integer llmAccountId;
+
+    /** 播放次数 */
+    @Excel(name = "播放次数")
+    private Integer playTimes;
+
+    /** 请求参数 */
+    @JsonInclude(JsonInclude.Include.NON_EMPTY)
+    private Map<String, Object> params = new HashMap<>();
+
+    /** 总名单量 */
+    private Integer phoneCount;
+
+    /** 未拨打名单量 */
+    private Integer noCallCount;
+
+    /** 已拨打名单量 */
+    private Integer callCount;
+
+    /** 接通名单量 */
+    private Integer connectCount;
+
+    /** 未接通名单量 */
+    private Integer noConnectCount;
+
+    /** 实际接通率 */
+    private Double realConnectRate;
+
+    /** 预估接通率 (百分数格式)*/
+    @Excel(name = "预估接通率")
+    private Integer conntectRate;
+
+//    /** tts provider */
+//    private String provider;
+
+    /** tts provider */
+    private String asrProvider;
+
+    /** aiTransferType */
+    private String aiTransferType;
+
+    /** aiTransferData */
+    private String aiTransferData;
+
+    /** aiTransferGroupId */
+    private String aiTransferGroupId;
+
+    /** aiTransferGatewayId */
+    private String aiTransferGatewayId;
+
+    /** aiTransferGatewayDestNumber */
+    private String aiTransferGatewayDestNumber;
+
+    /** aiTransferExtNumber */
+    private String aiTransferExtNumber;
+
+    /** 播放次数 */
+    @Excel(name = "播放次数")
+    private Integer autoStop;
+
+    /** ivrId */
+    private String ivrId;
+
+    /** 是否允许删除 */
+    private Integer allowDel;
+
+}

+ 68 - 0
fs-service/src/main/java/com/fs/aicall/domain/CcLlmAgentAccount.java

@@ -0,0 +1,68 @@
+package com.fs.aicall.domain;
+
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 机器人参数配置对象 cc_llm_agent_account
+ * 
+ * @author ruoyi
+ * @date 2025-06-16
+ */
+@Data
+public class CcLlmAgentAccount implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /** 主键id */
+    private Integer id;
+
+    /** 机器人名称 */
+    @Excel(name = "机器人名称")
+    private String name;
+
+    /** 机器人配置信息 */
+    @Excel(name = "机器人配置信息")
+    private String accountJson;
+
+    /** Entity class for storing account config info. */
+    @Excel(name = "Entity class for storing account config info.")
+    private String accountEntity;
+
+    /** 实现类 */
+    @Excel(name = "实现类")
+    private String providerClassName;
+
+    /** 是否打断(1:是,0:否) */
+    @Excel(name = "是否打断(1:是,0:否)")
+    private Integer interruptFlag;
+
+    /** 打断关键词列表 */
+    @Excel(name = "打断关键词列表")
+    private String interruptKeywords;
+
+    /** 打断忽略关键字列表 */
+    @Excel(name = "打断忽略关键字列表")
+    private String interruptIgnoreKeywords;
+
+    /** 客户意向提示词 */
+    @Excel(name = "客户意向提示词")
+    private String intentionTips;
+
+    /** 模型并发数 */
+    @Excel(name = "模型并发数")
+    private Integer concurrentNum;
+
+    /** 转人工的按键支持 */
+    @Excel(name = "转人工的按键支持")
+    private String transferManualDigit;
+
+    /** 知识库id */
+    private Integer kbCatId;
+
+    /** 模型ID列表,用于IN查询 */
+    private List<Long> modelIds;
+
+}

+ 29 - 0
fs-service/src/main/java/com/fs/aicall/domain/CcLlmAgentProvider.java

@@ -0,0 +1,29 @@
+package com.fs.aicall.domain;
+
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 大模型实现类列表对象 cc_llm_agent_provider
+ * 
+ * @author ruoyi
+ * @date 2025-06-16
+ */
+@Data
+public class CcLlmAgentProvider implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /** 主键id */
+    private Integer id;
+
+    /** 实现类 */
+    @Excel(name = "实现类")
+    private String providerClassName;
+
+    /** 备注 */
+    @Excel(name = "备注")
+    private String note;
+
+}

+ 31 - 0
fs-service/src/main/java/com/fs/aicall/domain/CcLlmKb.java

@@ -0,0 +1,31 @@
+package com.fs.aicall.domain;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+/**
+ * 知识库内容对象 cc_llm_kb
+ * 
+ * @author ruoyi
+ * @date 2026-01-19
+ */
+@Data
+@Accessors(chain = true)
+public class CcLlmKb implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /** 主键id */
+    private Long id;
+
+    /** kb title */
+    private String title;
+
+    /** 答案 */
+    private String content;
+
+    /** kb cat id. */
+    private Long catId;
+
+}

+ 31 - 0
fs-service/src/main/java/com/fs/aicall/domain/CcLlmKbCat.java

@@ -0,0 +1,31 @@
+package com.fs.aicall.domain;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+/**
+ * 知识库对象 cc_llm_kb_cat
+ * 
+ * @author ruoyi
+ * @date 2026-01-19
+ */
+@Data
+@Accessors(chain = true)
+public class CcLlmKbCat implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /** 主键id */
+    private Long id;
+
+    /** 分类 */
+    private String cat;
+
+    /** 描述 */
+    private String description;
+
+    /** 内容数量 */
+    private Integer contentCount;
+
+}

+ 45 - 0
fs-service/src/main/java/com/fs/aicall/domain/CcParams.java

@@ -0,0 +1,45 @@
+package com.fs.aicall.domain;
+
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+/**
+ * callcenter参数配置对象 cc_params
+ * 
+ * @author ruoyi
+ * @date 2025-04-21
+ */
+@Data
+@Accessors(chain = true)
+public class CcParams implements Serializable {
+    private static final long serialVersionUID = 1L;
+    /** 主键id */
+    private Long id;
+
+    /** 参数名 */
+    @Excel(name = "参数名")
+    private String paramName;
+
+    /** 参数编码 */
+    @Excel(name = "参数编码")
+    private String paramCode;
+
+    /** 参数值 */
+    @Excel(name = "参数值")
+    private String paramValue;
+
+    /** 参数类型 */
+    @Excel(name = "参数类型")
+    private String paramType;
+
+    /** 是否在网页上隐藏参数值 */
+    @Excel(name = "隐藏参数值")
+    private int hideValue;
+
+    /** 是否允许编辑 */
+    @Excel(name = "是否允许编辑")
+    private int allowEdit;
+}

+ 38 - 0
fs-service/src/main/java/com/fs/aicall/domain/CompanyBindAiModel.java

@@ -0,0 +1,38 @@
+package com.fs.aicall.domain;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.Date;
+
+/**
+ * 销售公司与AI模型绑定表
+ * 
+ * @author ruoyi
+ * @date 2026-03-20
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class CompanyBindAiModel extends BaseDomain
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 主键ID */
+    private Long id;
+
+    /** 销售公司ID */
+    private Long companyId;
+
+    /** 模型ID */
+    private Long modelId;
+
+    /** 创建时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date createTime;
+
+    /** 更新时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date updateTime;
+
+}

+ 82 - 0
fs-service/src/main/java/com/fs/aicall/mapper/CcCallTaskMapper.java

@@ -0,0 +1,82 @@
+package com.fs.aicall.mapper;
+
+import com.fs.aicall.domain.CcCallTask;
+import com.fs.common.annotation.DataSource;
+import com.fs.common.enums.DataSourceType;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 外呼任务Mapper接口
+ * 
+ * @author ruoyi
+ * @date 2025-05-29
+ */
+public interface CcCallTaskMapper 
+{
+    /**
+     * 查询外呼任务
+     * 
+     * @param batchId 外呼任务主键
+     * @return 外呼任务
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    public CcCallTask selectCcCallTaskByBatchId(Long batchId);
+
+    /**
+     * 查询外呼任务列表
+     * 
+     * @param ccCallTask 外呼任务
+     * @return 外呼任务集合
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    public List<CcCallTask> selectCcCallTaskList(CcCallTask ccCallTask);
+
+    /**
+     * 新增外呼任务
+     * 
+     * @param ccCallTask 外呼任务
+     * @return 结果
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    public int insertCcCallTask(CcCallTask ccCallTask);
+
+    /**
+     * 修改外呼任务
+     * 
+     * @param ccCallTask 外呼任务
+     * @return 结果
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    public int updateCcCallTask(CcCallTask ccCallTask);
+
+    /**
+     * 删除外呼任务
+     * 
+     * @param batchId 外呼任务主键
+     * @return 结果
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    public int deleteCcCallTaskByBatchId(Long batchId);
+
+    /**
+     * 批量删除外呼任务
+     * 
+     * @param batchIds 需要删除的数据主键集合
+     * @return 结果
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    public int deleteCcCallTaskByBatchIds(String[] batchIds);
+
+    /**
+     * 根据任务名称获取任务
+     * @param batchName
+     * @return
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    CcCallTask selectCcCallTaskByBatchName(@Param("batchName") String batchName, @Param("taskType") Integer taskType);
+
+    @DataSource(DataSourceType.EASYCALL)
+    void bakCallTaskByBatchId(Long batchId);
+}

+ 70 - 0
fs-service/src/main/java/com/fs/aicall/mapper/CcLlmAgentAccountMapper.java

@@ -0,0 +1,70 @@
+package com.fs.aicall.mapper;
+
+import com.fs.aicall.domain.CcLlmAgentAccount;
+import com.fs.common.annotation.DataSource;
+import com.fs.common.enums.DataSourceType;
+
+import java.util.List;
+
+/**
+ * 机器人参数配置Mapper接口
+ * 
+ * @author ruoyi
+ * @date 2025-06-16
+ */
+public interface CcLlmAgentAccountMapper 
+{
+    /**
+     * 查询机器人参数配置
+     * 
+     * @param id 机器人参数配置主键
+     * @return 机器人参数配置
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    public CcLlmAgentAccount selectCcLlmAgentAccountById(Integer id);
+
+    /**
+     * 查询机器人参数配置列表
+     * 
+     * @param ccLlmAgentAccount 机器人参数配置
+     * @return 机器人参数配置集合
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    public List<CcLlmAgentAccount> selectCcLlmAgentAccountList(CcLlmAgentAccount ccLlmAgentAccount);
+
+    /**
+     * 新增机器人参数配置
+     * 
+     * @param ccLlmAgentAccount 机器人参数配置
+     * @return 结果
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    public int insertCcLlmAgentAccount(CcLlmAgentAccount ccLlmAgentAccount);
+
+    /**
+     * 修改机器人参数配置
+     * 
+     * @param ccLlmAgentAccount 机器人参数配置
+     * @return 结果
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    public int updateCcLlmAgentAccount(CcLlmAgentAccount ccLlmAgentAccount);
+
+    /**
+     * 删除机器人参数配置
+     * 
+     * @param id 机器人参数配置主键
+     * @return 结果
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    public int deleteCcLlmAgentAccountById(Integer id);
+
+    /**
+     * 批量删除机器人参数配置
+     * 
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    public int deleteCcLlmAgentAccountByIds(String[] ids);
+}

+ 71 - 0
fs-service/src/main/java/com/fs/aicall/mapper/CcLlmAgentProviderMapper.java

@@ -0,0 +1,71 @@
+package com.fs.aicall.mapper;
+
+
+import com.fs.aicall.domain.CcLlmAgentProvider;
+import com.fs.common.annotation.DataSource;
+import com.fs.common.enums.DataSourceType;
+
+import java.util.List;
+
+/**
+ * 大模型实现类列表Mapper接口
+ * 
+ * @author ruoyi
+ * @date 2025-06-16
+ */
+public interface CcLlmAgentProviderMapper 
+{
+    /**
+     * 查询大模型实现类列表
+     * 
+     * @param id 大模型实现类列表主键
+     * @return 大模型实现类列表
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    public CcLlmAgentProvider selectCcLlmAgentProviderById(Integer id);
+
+    /**
+     * 查询大模型实现类列表列表
+     * 
+     * @param ccLlmAgentProvider 大模型实现类列表
+     * @return 大模型实现类列表集合
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    public List<CcLlmAgentProvider> selectCcLlmAgentProviderList(CcLlmAgentProvider ccLlmAgentProvider);
+
+    /**
+     * 新增大模型实现类列表
+     * 
+     * @param ccLlmAgentProvider 大模型实现类列表
+     * @return 结果
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    public int insertCcLlmAgentProvider(CcLlmAgentProvider ccLlmAgentProvider);
+
+    /**
+     * 修改大模型实现类列表
+     * 
+     * @param ccLlmAgentProvider 大模型实现类列表
+     * @return 结果
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    public int updateCcLlmAgentProvider(CcLlmAgentProvider ccLlmAgentProvider);
+
+    /**
+     * 删除大模型实现类列表
+     * 
+     * @param id 大模型实现类列表主键
+     * @return 结果
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    public int deleteCcLlmAgentProviderById(Integer id);
+
+    /**
+     * 批量删除大模型实现类列表
+     * 
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    public int deleteCcLlmAgentProviderByIds(String[] ids);
+}

+ 70 - 0
fs-service/src/main/java/com/fs/aicall/mapper/CcLlmKbCatMapper.java

@@ -0,0 +1,70 @@
+package com.fs.aicall.mapper;
+
+import com.fs.aicall.domain.CcLlmKbCat;
+import com.fs.common.annotation.DataSource;
+import com.fs.common.enums.DataSourceType;
+
+import java.util.List;
+
+/**
+ * 知识库Mapper接口
+ * 
+ * @author ruoyi
+ * @date 2026-01-19
+ */
+public interface CcLlmKbCatMapper 
+{
+    /**
+     * 查询知识库
+     * 
+     * @param id 知识库主键
+     * @return 知识库
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    public CcLlmKbCat selectCcLlmKbCatById(Long id);
+
+    /**
+     * 查询知识库列表
+     * 
+     * @param ccLlmKbCat 知识库
+     * @return 知识库集合
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    public List<CcLlmKbCat> selectCcLlmKbCatList(CcLlmKbCat ccLlmKbCat);
+
+    /**
+     * 新增知识库
+     * 
+     * @param ccLlmKbCat 知识库
+     * @return 结果
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    public int insertCcLlmKbCat(CcLlmKbCat ccLlmKbCat);
+
+    /**
+     * 修改知识库
+     * 
+     * @param ccLlmKbCat 知识库
+     * @return 结果
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    public int updateCcLlmKbCat(CcLlmKbCat ccLlmKbCat);
+
+    /**
+     * 删除知识库
+     * 
+     * @param id 知识库主键
+     * @return 结果
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    public int deleteCcLlmKbCatById(Long id);
+
+    /**
+     * 批量删除知识库
+     * 
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    public int deleteCcLlmKbCatByIds(String[] ids);
+}

+ 79 - 0
fs-service/src/main/java/com/fs/aicall/mapper/CcLlmKbMapper.java

@@ -0,0 +1,79 @@
+package com.fs.aicall.mapper;
+
+import com.fs.aicall.domain.CcLlmKb;
+import com.fs.common.annotation.DataSource;
+import com.fs.common.enums.DataSourceType;
+
+import java.util.List;
+
+/**
+ * 知识库内容Mapper接口
+ * 
+ * @author ruoyi
+ * @date 2026-01-19
+ */
+public interface CcLlmKbMapper 
+{
+    /**
+     * 查询知识库内容
+     * 
+     * @param id 知识库内容主键
+     * @return 知识库内容
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    public CcLlmKb selectCcLlmKbById(Long id);
+
+    /**
+     * 查询知识库内容列表
+     * 
+     * @param ccLlmKb 知识库内容
+     * @return 知识库内容集合
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    public List<CcLlmKb> selectCcLlmKbList(CcLlmKb ccLlmKb);
+
+    /**
+     * 新增知识库内容
+     * 
+     * @param ccLlmKb 知识库内容
+     * @return 结果
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    public int insertCcLlmKb(CcLlmKb ccLlmKb);
+
+    /**
+     * 修改知识库内容
+     * 
+     * @param ccLlmKb 知识库内容
+     * @return 结果
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    public int updateCcLlmKb(CcLlmKb ccLlmKb);
+
+    /**
+     * 删除知识库内容
+     * 
+     * @param id 知识库内容主键
+     * @return 结果
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    public int deleteCcLlmKbById(Long id);
+
+    /**
+     * 批量删除知识库内容
+     * 
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    public int deleteCcLlmKbByIds(String[] ids);
+
+    @DataSource(DataSourceType.EASYCALL)
+    Integer selectCountByCatId(Long catId);
+
+    @DataSource(DataSourceType.EASYCALL)
+    void deleteCcLlmKbByCatId(Long catId);
+
+    @DataSource(DataSourceType.EASYCALL)
+    void insertBatch(List<CcLlmKb> ccLlmKbList);
+}

+ 79 - 0
fs-service/src/main/java/com/fs/aicall/mapper/CcParamsMapper.java

@@ -0,0 +1,79 @@
+package com.fs.aicall.mapper;
+
+import com.fs.aicall.domain.CcParams;
+import com.fs.common.annotation.DataSource;
+import com.fs.common.enums.DataSourceType;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * callcenter参数配置Mapper接口
+ * 
+ * @author ruoyi
+ * @date 2025-04-21
+ */
+public interface CcParamsMapper 
+{
+    /**
+     * 查询callcenter参数配置
+     * 
+     * @param id callcenter参数配置主键
+     * @return callcenter参数配置
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    public CcParams selectCcParamsById(Long id);
+
+    /**
+     * 查询callcenter参数配置列表
+     * 
+     * @param ccParams callcenter参数配置
+     * @return callcenter参数配置集合
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    public List<CcParams> selectCcParamsList(CcParams ccParams);
+
+    /**
+     * 新增callcenter参数配置
+     * 
+     * @param ccParams callcenter参数配置
+     * @return 结果
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    public int insertCcParams(CcParams ccParams);
+
+    /**
+     * 修改callcenter参数配置
+     * 
+     * @param ccParams callcenter参数配置
+     * @return 结果
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    public int updateCcParams(CcParams ccParams);
+
+    /**
+     * 删除callcenter参数配置
+     * 
+     * @param id callcenter参数配置主键
+     * @return 结果
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    public int deleteCcParamsById(Long id);
+
+    /**
+     * 批量删除callcenter参数配置
+     * 
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    public int deleteCcParamsByIds(String[] ids);
+
+    /**
+     *
+     * @param paramCode
+     * @param paramValue
+     */
+    @DataSource(DataSourceType.EASYCALL)
+    void updateParamsValue(@Param("paramCode") String paramCode, @Param("paramValue") String paramValue);
+}

+ 90 - 0
fs-service/src/main/java/com/fs/aicall/mapper/CompanyBindAiModelMapper.java

@@ -0,0 +1,90 @@
+package com.fs.aicall.mapper;
+
+import com.fs.aicall.domain.CompanyBindAiModel;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 销售公司与AI模型绑定表Mapper接口
+ * 
+ * @author ruoyi
+ * @date 2026-03-20
+ */
+@Mapper
+public interface CompanyBindAiModelMapper
+{
+    /**
+     * 查询销售公司与AI模型绑定表
+     * 
+     * @param id 销售公司与AI模型绑定表主键
+     * @return 销售公司与AI模型绑定表
+     */
+    public CompanyBindAiModel selectCompanyBindAiModelById(Long id);
+
+    /**
+     * 查询销售公司与AI模型绑定表列表
+     * 
+     * @param companyBindAiModel 销售公司与AI模型绑定表
+     * @return 销售公司与AI模型绑定表集合
+     */
+    public List<CompanyBindAiModel> selectCompanyBindAiModelList(CompanyBindAiModel companyBindAiModel);
+
+    /**
+     * 新增销售公司与AI模型绑定表
+     * 
+     * @param companyBindAiModel 销售公司与AI模型绑定表
+     * @return 结果
+     */
+    public int insertCompanyBindAiModel(CompanyBindAiModel companyBindAiModel);
+
+    /**
+     * 修改销售公司与AI模型绑定表
+     * 
+     * @param companyBindAiModel 销售公司与AI模型绑定表
+     * @return 结果
+     */
+    public int updateCompanyBindAiModel(CompanyBindAiModel companyBindAiModel);
+
+    /**
+     * 删除销售公司与AI模型绑定表
+     * 
+     * @param id 销售公司与AI模型绑定表主键
+     * @return 结果
+     */
+    public int deleteCompanyBindAiModelById(Long id);
+
+    /**
+     * 批量删除销售公司与AI模型绑定表
+     * 
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    public int deleteCompanyBindAiModelByIds(Long[] ids);
+
+    /**
+     * 根据公司ID查询绑定的模型ID列表
+     * 
+     * @param companyId 公司ID
+     * @return 模型ID列表
+     */
+    public List<Long> selectModelIdsByCompanyId(Long companyId);
+
+    /**
+     * 根据模型ID删除绑定关系
+     * 
+     * @param modelId 模型ID
+     * @return 结果
+     */
+    public int deleteCompanyBindAiModelByModelId(Long modelId);
+
+    /**
+     * 批量插入绑定关系
+     * 
+     * @param companyId 公司ID
+     * @param modelIds 模型ID列表
+     * @return 结果
+     */
+    public int batchInsertCompanyBindAiModel(@Param("companyId") Long companyId, @Param("modelIds") List<Long> modelIds);
+}

+ 71 - 0
fs-service/src/main/java/com/fs/aicall/service/ICcCallTaskService.java

@@ -0,0 +1,71 @@
+package com.fs.aicall.service;
+
+import com.fs.aicall.domain.CcCallTask;
+
+import java.util.List;
+
+/**
+ * 外呼任务Service接口
+ * 
+ * @author ruoyi
+ * @date 2025-05-29
+ */
+public interface ICcCallTaskService 
+{
+    /**
+     * 查询外呼任务
+     * 
+     * @param batchId 外呼任务主键
+     * @return 外呼任务
+     */
+    public CcCallTask selectCcCallTaskByBatchId(Long batchId);
+
+    /**
+     * 查询外呼任务列表
+     * 
+     * @param ccCallTask 外呼任务
+     * @return 外呼任务集合
+     */
+    public List<CcCallTask> selectCcCallTaskList(CcCallTask ccCallTask);
+
+    /**
+     * 新增外呼任务
+     * 
+     * @param ccCallTask 外呼任务
+     * @return 结果
+     */
+    public int insertCcCallTask(CcCallTask ccCallTask);
+
+    /**
+     * 修改外呼任务
+     * 
+     * @param ccCallTask 外呼任务
+     * @return 结果
+     */
+    public int updateCcCallTask(CcCallTask ccCallTask);
+
+    /**
+     * 批量删除外呼任务
+     * 
+     * @param batchIds 需要删除的外呼任务主键集合
+     * @return 结果
+     */
+    public int deleteCcCallTaskByBatchIds(String batchIds);
+
+    /**
+     * 删除外呼任务信息
+     * 
+     * @param batchId 外呼任务主键
+     * @return 结果
+     */
+    public int deleteCcCallTaskByBatchId(Long batchId);
+
+    /**
+     * 根据任务名称获取任务
+     * @param batchName
+     * @return
+     */
+    CcCallTask selectCcCallTaskByBatchName(String batchName, Integer taskType);
+
+    void bakCallTaskByBatchId(Long batchId);
+}

+ 62 - 0
fs-service/src/main/java/com/fs/aicall/service/ICcLlmAgentAccountService.java

@@ -0,0 +1,62 @@
+package com.fs.aicall.service;
+
+import com.fs.aicall.domain.CcLlmAgentAccount;
+
+import java.util.List;
+
+/**
+ * 机器人参数配置Service接口
+ * 
+ * @author ruoyi
+ * @date 2025-06-16
+ */
+public interface ICcLlmAgentAccountService 
+{
+    /**
+     * 查询机器人参数配置
+     * 
+     * @param id 机器人参数配置主键
+     * @return 机器人参数配置
+     */
+    public CcLlmAgentAccount selectCcLlmAgentAccountById(Integer id);
+
+    /**
+     * 查询机器人参数配置列表
+     * 
+     * @param ccLlmAgentAccount 机器人参数配置
+     * @return 机器人参数配置集合
+     */
+    public List<CcLlmAgentAccount> selectCcLlmAgentAccountList(CcLlmAgentAccount ccLlmAgentAccount);
+
+    /**
+     * 新增机器人参数配置
+     * 
+     * @param ccLlmAgentAccount 机器人参数配置
+     * @return 结果
+     */
+    public int insertCcLlmAgentAccount(CcLlmAgentAccount ccLlmAgentAccount);
+
+    /**
+     * 修改机器人参数配置
+     * 
+     * @param ccLlmAgentAccount 机器人参数配置
+     * @return 结果
+     */
+    public int updateCcLlmAgentAccount(CcLlmAgentAccount ccLlmAgentAccount);
+
+    /**
+     * 批量删除机器人参数配置
+     * 
+     * @param ids 需要删除的机器人参数配置主键集合
+     * @return 结果
+     */
+    public int deleteCcLlmAgentAccountByIds(String ids);
+
+    /**
+     * 删除机器人参数配置信息
+     * 
+     * @param id 机器人参数配置主键
+     * @return 结果
+     */
+    public int deleteCcLlmAgentAccountById(Integer id);
+}

+ 63 - 0
fs-service/src/main/java/com/fs/aicall/service/ICcLlmAgentProviderService.java

@@ -0,0 +1,63 @@
+package com.fs.aicall.service;
+
+
+import com.fs.aicall.domain.CcLlmAgentProvider;
+
+import java.util.List;
+
+/**
+ * 大模型实现类列表Service接口
+ * 
+ * @author ruoyi
+ * @date 2025-06-16
+ */
+public interface ICcLlmAgentProviderService 
+{
+    /**
+     * 查询大模型实现类列表
+     * 
+     * @param id 大模型实现类列表主键
+     * @return 大模型实现类列表
+     */
+    public CcLlmAgentProvider selectCcLlmAgentProviderById(Integer id);
+
+    /**
+     * 查询大模型实现类列表列表
+     * 
+     * @param ccLlmAgentProvider 大模型实现类列表
+     * @return 大模型实现类列表集合
+     */
+    public List<CcLlmAgentProvider> selectCcLlmAgentProviderList(CcLlmAgentProvider ccLlmAgentProvider);
+
+    /**
+     * 新增大模型实现类列表
+     * 
+     * @param ccLlmAgentProvider 大模型实现类列表
+     * @return 结果
+     */
+    public int insertCcLlmAgentProvider(CcLlmAgentProvider ccLlmAgentProvider);
+
+    /**
+     * 修改大模型实现类列表
+     * 
+     * @param ccLlmAgentProvider 大模型实现类列表
+     * @return 结果
+     */
+    public int updateCcLlmAgentProvider(CcLlmAgentProvider ccLlmAgentProvider);
+
+    /**
+     * 批量删除大模型实现类列表
+     * 
+     * @param ids 需要删除的大模型实现类列表主键集合
+     * @return 结果
+     */
+    public int deleteCcLlmAgentProviderByIds(String ids);
+
+    /**
+     * 删除大模型实现类列表信息
+     * 
+     * @param id 大模型实现类列表主键
+     * @return 结果
+     */
+    public int deleteCcLlmAgentProviderById(Integer id);
+}

+ 71 - 0
fs-service/src/main/java/com/fs/aicall/service/ICcLlmKbCatService.java

@@ -0,0 +1,71 @@
+package com.fs.aicall.service;
+
+
+import com.fs.aicall.domain.CcLlmKbCat;
+
+import java.util.List;
+
+/**
+ * 知识库Service接口
+ * 
+ * @author ruoyi
+ * @date 2026-01-19
+ */
+public interface ICcLlmKbCatService 
+{
+    /**
+     * 查询知识库
+     * 
+     * @param id 知识库主键
+     * @return 知识库
+     */
+    public CcLlmKbCat selectCcLlmKbCatById(Long id);
+
+    /**
+     * 查询知识库列表
+     * 
+     * @param ccLlmKbCat 知识库
+     * @return 知识库集合
+     */
+    public List<CcLlmKbCat> selectCcLlmKbCatList(CcLlmKbCat ccLlmKbCat);
+
+    /**
+     * 新增知识库
+     * 
+     * @param ccLlmKbCat 知识库
+     * @return 结果
+     */
+    public int insertCcLlmKbCat(CcLlmKbCat ccLlmKbCat);
+
+    /**
+     * 修改知识库
+     * 
+     * @param ccLlmKbCat 知识库
+     * @return 结果
+     */
+    public int updateCcLlmKbCat(CcLlmKbCat ccLlmKbCat);
+
+    /**
+     * 批量删除知识库
+     * 
+     * @param ids 需要删除的知识库主键集合
+     * @return 结果
+     */
+    public int deleteCcLlmKbCatByIds(String ids);
+
+    /**
+     * 删除知识库信息
+     * 
+     * @param id 知识库主键
+     * @return 结果
+     */
+    public int deleteCcLlmKbCatById(Long id);
+
+    /**
+     *
+     * @param id
+     * @param cat
+     * @return
+     */
+    CcLlmKbCat selectCcLlmKbCatByCat(Long id, String cat);
+}

+ 70 - 0
fs-service/src/main/java/com/fs/aicall/service/ICcLlmKbService.java

@@ -0,0 +1,70 @@
+package com.fs.aicall.service;
+
+import com.fs.aicall.domain.CcLlmKb;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.List;
+
+/**
+ * 知识库内容Service接口
+ * 
+ * @author ruoyi
+ * @date 2026-01-19
+ */
+public interface ICcLlmKbService 
+{
+    /**
+     * 查询知识库内容
+     * 
+     * @param id 知识库内容主键
+     * @return 知识库内容
+     */
+    public CcLlmKb selectCcLlmKbById(Long id);
+
+    /**
+     * 查询知识库内容列表
+     * 
+     * @param ccLlmKb 知识库内容
+     * @return 知识库内容集合
+     */
+    public List<CcLlmKb> selectCcLlmKbList(CcLlmKb ccLlmKb);
+
+    /**
+     * 新增知识库内容
+     * 
+     * @param ccLlmKb 知识库内容
+     * @return 结果
+     */
+    public int insertCcLlmKb(CcLlmKb ccLlmKb);
+
+    /**
+     * 修改知识库内容
+     * 
+     * @param ccLlmKb 知识库内容
+     * @return 结果
+     */
+    public int updateCcLlmKb(CcLlmKb ccLlmKb);
+
+    /**
+     * 批量删除知识库内容
+     * 
+     * @param ids 需要删除的知识库内容主键集合
+     * @return 结果
+     */
+    public int deleteCcLlmKbByIds(String ids);
+
+    /**
+     * 删除知识库内容信息
+     * 
+     * @param id 知识库内容主键
+     * @return 结果
+     */
+    public int deleteCcLlmKbById(Long id);
+
+    Integer selectCountByCatId(Long catId);
+
+    String importData(MultipartFile file,  Long fixedCatId) throws Exception;
+
+    CcLlmKb selectCcLlmKbContentByTitle(Long id, String title, Long catId);
+    
+}

+ 83 - 0
fs-service/src/main/java/com/fs/aicall/service/ICcParamsService.java

@@ -0,0 +1,83 @@
+package com.fs.aicall.service;
+
+
+import com.fs.aicall.domain.CcParams;
+
+import java.util.List;
+
+/**
+ * callcenter参数配置Service接口
+ * 
+ * @author ruoyi
+ * @date 2025-04-21
+ */
+public interface ICcParamsService 
+{
+    /**
+     * 查询callcenter参数配置
+     * 
+     * @param id callcenter参数配置主键
+     * @return callcenter参数配置
+     */
+    public CcParams selectCcParamsById(Long id);
+
+    /**
+     * 查询callcenter参数配置列表
+     * 
+     * @param ccParams callcenter参数配置
+     * @return callcenter参数配置集合
+     */
+    public List<CcParams> selectCcParamsList(CcParams ccParams);
+
+    /**
+     * 新增callcenter参数配置
+     * 
+     * @param ccParams callcenter参数配置
+     * @return 结果
+     */
+    public int insertCcParams(CcParams ccParams);
+
+    /**
+     * 修改callcenter参数配置
+     * 
+     * @param ccParams callcenter参数配置
+     * @return 结果
+     */
+    public int updateCcParams(CcParams ccParams);
+
+    /**
+     * 批量删除callcenter参数配置
+     * 
+     * @param ids 需要删除的callcenter参数配置主键集合
+     * @return 结果
+     */
+    public int deleteCcParamsByIds(String ids);
+
+    /**
+     * 删除callcenter参数配置信息
+     * 
+     * @param id callcenter参数配置主键
+     * @return 结果
+     */
+    public int deleteCcParamsById(Long id);
+
+    /**
+     * 根据参数编号获取参数值
+     * @param paramCode
+     * @return
+     */
+    String getParamValueByCode(String paramCode, String defaultValue);
+
+    /**
+     * 更新参数值
+     * @param paramCode
+     * @param paramValue
+     */
+    void updateParamsValue(String paramCode, String paramValue);
+
+    /**
+     *
+     * @return
+     */
+    String reloadParams();
+}

+ 105 - 0
fs-service/src/main/java/com/fs/aicall/service/ICompanyBindAiModelService.java

@@ -0,0 +1,105 @@
+package com.fs.aicall.service;
+
+import com.fs.aicall.domain.CompanyBindAiModel;
+
+import java.util.List;
+
+/**
+ * 销售公司与AI模型绑定表Service接口
+ * 
+ * @author ruoyi
+ * @date 2026-03-20
+ */
+public interface ICompanyBindAiModelService
+{
+    /**
+     * 查询销售公司与AI模型绑定表
+     * 
+     * @param id 销售公司与AI模型绑定表主键
+     * @return 销售公司与AI模型绑定表
+     */
+    public CompanyBindAiModel selectCompanyBindAiModelById(Long id);
+
+    /**
+     * 查询销售公司与AI模型绑定表列表
+     * 
+     * @param companyBindAiModel 销售公司与AI模型绑定表
+     * @return 销售公司与AI模型绑定表集合
+     */
+    public List<CompanyBindAiModel> selectCompanyBindAiModelList(CompanyBindAiModel companyBindAiModel);
+
+    /**
+     * 新增销售公司与AI模型绑定表
+     * 
+     * @param companyBindAiModel 销售公司与AI模型绑定表
+     * @return 结果
+     */
+    public int insertCompanyBindAiModel(CompanyBindAiModel companyBindAiModel);
+
+    /**
+     * 修改销售公司与AI模型绑定表
+     * 
+     * @param companyBindAiModel 销售公司与AI模型绑定表
+     * @return 结果
+     */
+    public int updateCompanyBindAiModel(CompanyBindAiModel companyBindAiModel);
+
+    /**
+     * 删除销售公司与AI模型绑定表
+     * 
+     * @param id 销售公司与AI模型绑定表主键
+     * @return 结果
+     */
+    public int deleteCompanyBindAiModelById(Long id);
+
+    /**
+     * 批量删除销售公司与AI模型绑定表
+     * 
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    public int deleteCompanyBindAiModelByIds(Long[] ids);
+
+    /**
+     * 根据公司ID查询绑定的模型ID列表
+     * 
+     * @param companyId 公司ID
+     * @return 模型ID列表
+     */
+    public List<Long> selectModelIdsByCompanyId(Long companyId);
+
+    /**
+     * 根据模型ID删除绑定关系
+     * 
+     * @param modelId 模型ID
+     * @return 结果
+     */
+    public int deleteCompanyBindAiModelByModelId(Long modelId);
+
+    /**
+     * 批量插入绑定关系
+     * 
+     * @param companyId 公司ID
+     * @param modelIds 模型ID列表
+     * @return 结果
+     */
+    public int batchInsertCompanyBindAiModel(Long companyId, List<Long> modelIds);
+
+    /**
+     * 为模型添加公司绑定
+     * 
+     * @param modelId 模型ID
+     * @param companyId 公司ID
+     * @return 结果
+     */
+    public int bindCompanyToModel(Long modelId, Long companyId);
+
+    /**
+     * 为模型批量添加公司绑定
+     * 
+     * @param modelId 模型ID
+     * @param companyIds 公司ID列表
+     * @return 结果
+     */
+    public int batchBindCompaniesToModel(Long modelId, List<Long> companyIds);
+}

+ 118 - 0
fs-service/src/main/java/com/fs/aicall/service/impl/CcCallTaskServiceImpl.java

@@ -0,0 +1,118 @@
+package com.fs.aicall.service.impl;
+
+import com.fs.aicall.domain.CcCallTask;
+import com.fs.aicall.mapper.CcCallTaskMapper;
+import com.fs.aicall.service.ICcCallTaskService;
+import com.fs.common.core.text.Convert;
+import com.fs.common.utils.DateUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 外呼任务Service业务层处理
+ * 
+ * @author ruoyi
+ * @date 2025-05-29
+ */
+@Service
+public class CcCallTaskServiceImpl implements ICcCallTaskService
+{
+    @Autowired
+    private CcCallTaskMapper ccCallTaskMapper;
+
+    /**
+     * 查询外呼任务
+     * 
+     * @param batchId 外呼任务主键
+     * @return 外呼任务
+     */
+    @Override
+    public CcCallTask selectCcCallTaskByBatchId(Long batchId)
+    {
+        return ccCallTaskMapper.selectCcCallTaskByBatchId(batchId);
+    }
+
+    /**
+     * 查询外呼任务列表
+     * 
+     * @param ccCallTask 外呼任务
+     * @return 外呼任务
+     */
+    @Override
+    public List<CcCallTask> selectCcCallTaskList(CcCallTask ccCallTask)
+    {
+
+        Map<String, Object> params = ccCallTask.getParams();
+        if (null != params.get("createTimeStart")
+                && !"".equals(params.get("createTimeStart"))) {
+            params.put("createTimeStart", DateUtils.dateTime("yyyy-MM-dd HH:mm:ss", (String)params.get("createTimeStart")).getTime());
+        }
+        if (null != params.get("createTimeEnd")
+                && !"".equals(params.get("createTimeEnd"))) {
+            params.put("createTimeEnd", DateUtils.dateTime("yyyy-MM-dd HH:mm:ss", (String)params.get("createTimeEnd")).getTime());
+        }
+        ccCallTask.setParams(params);
+        return ccCallTaskMapper.selectCcCallTaskList(ccCallTask);
+    }
+
+    /**
+     * 新增外呼任务
+     * 
+     * @param ccCallTask 外呼任务
+     * @return 结果
+     */
+    @Override
+    public int insertCcCallTask(CcCallTask ccCallTask)
+    {
+        return ccCallTaskMapper.insertCcCallTask(ccCallTask);
+    }
+
+    /**
+     * 修改外呼任务
+     * 
+     * @param ccCallTask 外呼任务
+     * @return 结果
+     */
+    @Override
+    public int updateCcCallTask(CcCallTask ccCallTask)
+    {
+        return ccCallTaskMapper.updateCcCallTask(ccCallTask);
+    }
+
+    /**
+     * 批量删除外呼任务
+     * 
+     * @param batchIds 需要删除的外呼任务主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCcCallTaskByBatchIds(String batchIds)
+    {
+        return ccCallTaskMapper.deleteCcCallTaskByBatchIds(Convert.toStrArray(batchIds));
+    }
+
+    /**
+     * 删除外呼任务信息
+     * 
+     * @param batchId 外呼任务主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCcCallTaskByBatchId(Long batchId)
+    {
+        return ccCallTaskMapper.deleteCcCallTaskByBatchId(batchId);
+    }
+
+    @Override
+    public CcCallTask selectCcCallTaskByBatchName(String batchName, Integer taskType) {
+        return ccCallTaskMapper.selectCcCallTaskByBatchName(batchName, taskType);
+    }
+
+    @Override
+    public void bakCallTaskByBatchId(Long batchId) {
+        ccCallTaskMapper.bakCallTaskByBatchId(batchId);
+    }
+}

+ 95 - 0
fs-service/src/main/java/com/fs/aicall/service/impl/CcLlmAgentAccountServiceImpl.java

@@ -0,0 +1,95 @@
+package com.fs.aicall.service.impl;
+
+import com.fs.aicall.domain.CcLlmAgentAccount;
+import com.fs.aicall.mapper.CcLlmAgentAccountMapper;
+import com.fs.aicall.service.ICcLlmAgentAccountService;
+import com.fs.common.core.text.Convert;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 机器人参数配置Service业务层处理
+ * 
+ * @author ruoyi
+ * @date 2025-06-16
+ */
+@Service
+public class CcLlmAgentAccountServiceImpl implements ICcLlmAgentAccountService
+{
+    @Autowired
+    private CcLlmAgentAccountMapper ccLlmAgentAccountMapper;
+
+    /**
+     * 查询机器人参数配置
+     * 
+     * @param id 机器人参数配置主键
+     * @return 机器人参数配置
+     */
+    @Override
+    public CcLlmAgentAccount selectCcLlmAgentAccountById(Integer id)
+    {
+        return ccLlmAgentAccountMapper.selectCcLlmAgentAccountById(id);
+    }
+
+    /**
+     * 查询机器人参数配置列表
+     * 
+     * @param ccLlmAgentAccount 机器人参数配置
+     * @return 机器人参数配置
+     */
+    @Override
+    public List<CcLlmAgentAccount> selectCcLlmAgentAccountList(CcLlmAgentAccount ccLlmAgentAccount)
+    {
+        return ccLlmAgentAccountMapper.selectCcLlmAgentAccountList(ccLlmAgentAccount);
+    }
+
+    /**
+     * 新增机器人参数配置
+     * 
+     * @param ccLlmAgentAccount 机器人参数配置
+     * @return 结果
+     */
+    @Override
+    public int insertCcLlmAgentAccount(CcLlmAgentAccount ccLlmAgentAccount)
+    {
+        return ccLlmAgentAccountMapper.insertCcLlmAgentAccount(ccLlmAgentAccount);
+    }
+
+    /**
+     * 修改机器人参数配置
+     * 
+     * @param ccLlmAgentAccount 机器人参数配置
+     * @return 结果
+     */
+    @Override
+    public int updateCcLlmAgentAccount(CcLlmAgentAccount ccLlmAgentAccount)
+    {
+        return ccLlmAgentAccountMapper.updateCcLlmAgentAccount(ccLlmAgentAccount);
+    }
+
+    /**
+     * 批量删除机器人参数配置
+     * 
+     * @param ids 需要删除的机器人参数配置主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCcLlmAgentAccountByIds(String ids)
+    {
+        return ccLlmAgentAccountMapper.deleteCcLlmAgentAccountByIds(Convert.toStrArray(ids));
+    }
+
+    /**
+     * 删除机器人参数配置信息
+     * 
+     * @param id 机器人参数配置主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCcLlmAgentAccountById(Integer id)
+    {
+        return ccLlmAgentAccountMapper.deleteCcLlmAgentAccountById(id);
+    }
+}

+ 95 - 0
fs-service/src/main/java/com/fs/aicall/service/impl/CcLlmAgentProviderServiceImpl.java

@@ -0,0 +1,95 @@
+package com.fs.aicall.service.impl;
+
+import com.fs.aicall.domain.CcLlmAgentProvider;
+import com.fs.aicall.mapper.CcLlmAgentProviderMapper;
+import com.fs.aicall.service.ICcLlmAgentProviderService;
+import com.fs.common.core.text.Convert;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 大模型实现类列表Service业务层处理
+ * 
+ * @author ruoyi
+ * @date 2025-06-16
+ */
+@Service
+public class CcLlmAgentProviderServiceImpl implements ICcLlmAgentProviderService
+{
+    @Autowired
+    private CcLlmAgentProviderMapper ccLlmAgentProviderMapper;
+
+    /**
+     * 查询大模型实现类列表
+     * 
+     * @param id 大模型实现类列表主键
+     * @return 大模型实现类列表
+     */
+    @Override
+    public CcLlmAgentProvider selectCcLlmAgentProviderById(Integer id)
+    {
+        return ccLlmAgentProviderMapper.selectCcLlmAgentProviderById(id);
+    }
+
+    /**
+     * 查询大模型实现类列表列表
+     * 
+     * @param ccLlmAgentProvider 大模型实现类列表
+     * @return 大模型实现类列表
+     */
+    @Override
+    public List<CcLlmAgentProvider> selectCcLlmAgentProviderList(CcLlmAgentProvider ccLlmAgentProvider)
+    {
+        return ccLlmAgentProviderMapper.selectCcLlmAgentProviderList(ccLlmAgentProvider);
+    }
+
+    /**
+     * 新增大模型实现类列表
+     * 
+     * @param ccLlmAgentProvider 大模型实现类列表
+     * @return 结果
+     */
+    @Override
+    public int insertCcLlmAgentProvider(CcLlmAgentProvider ccLlmAgentProvider)
+    {
+        return ccLlmAgentProviderMapper.insertCcLlmAgentProvider(ccLlmAgentProvider);
+    }
+
+    /**
+     * 修改大模型实现类列表
+     * 
+     * @param ccLlmAgentProvider 大模型实现类列表
+     * @return 结果
+     */
+    @Override
+    public int updateCcLlmAgentProvider(CcLlmAgentProvider ccLlmAgentProvider)
+    {
+        return ccLlmAgentProviderMapper.updateCcLlmAgentProvider(ccLlmAgentProvider);
+    }
+
+    /**
+     * 批量删除大模型实现类列表
+     * 
+     * @param ids 需要删除的大模型实现类列表主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCcLlmAgentProviderByIds(String ids)
+    {
+        return ccLlmAgentProviderMapper.deleteCcLlmAgentProviderByIds(Convert.toStrArray(ids));
+    }
+
+    /**
+     * 删除大模型实现类列表信息
+     * 
+     * @param id 大模型实现类列表主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCcLlmAgentProviderById(Integer id)
+    {
+        return ccLlmAgentProviderMapper.deleteCcLlmAgentProviderById(id);
+    }
+}

+ 113 - 0
fs-service/src/main/java/com/fs/aicall/service/impl/CcLlmKbCatServiceImpl.java

@@ -0,0 +1,113 @@
+package com.fs.aicall.service.impl;
+
+import com.fs.aicall.domain.CcLlmKbCat;
+import com.fs.aicall.mapper.CcLlmKbCatMapper;
+import com.fs.aicall.service.ICcLlmKbCatService;
+import com.fs.common.core.text.Convert;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+
+import java.util.List;
+
+/**
+ * 知识库Service业务层处理
+ * 
+ * @author ruoyi
+ * @date 2026-01-19
+ */
+@Service
+public class CcLlmKbCatServiceImpl implements ICcLlmKbCatService
+{
+    @Autowired
+    private CcLlmKbCatMapper ccLlmKbCatMapper;
+
+    /**
+     * 查询知识库
+     * 
+     * @param id 知识库主键
+     * @return 知识库
+     */
+    @Override
+    public CcLlmKbCat selectCcLlmKbCatById(Long id)
+    {
+        return ccLlmKbCatMapper.selectCcLlmKbCatById(id);
+    }
+
+    /**
+     * 查询知识库列表
+     * 
+     * @param ccLlmKbCat 知识库
+     * @return 知识库
+     */
+    @Override
+    public List<CcLlmKbCat> selectCcLlmKbCatList(CcLlmKbCat ccLlmKbCat)
+    {
+        return ccLlmKbCatMapper.selectCcLlmKbCatList(ccLlmKbCat);
+    }
+
+    /**
+     * 新增知识库
+     * 
+     * @param ccLlmKbCat 知识库
+     * @return 结果
+     */
+    @Override
+    public int insertCcLlmKbCat(CcLlmKbCat ccLlmKbCat)
+    {
+        return ccLlmKbCatMapper.insertCcLlmKbCat(ccLlmKbCat);
+    }
+
+    /**
+     * 修改知识库
+     * 
+     * @param ccLlmKbCat 知识库
+     * @return 结果
+     */
+    @Override
+    public int updateCcLlmKbCat(CcLlmKbCat ccLlmKbCat)
+    {
+        return ccLlmKbCatMapper.updateCcLlmKbCat(ccLlmKbCat);
+    }
+
+    /**
+     * 批量删除知识库
+     * 
+     * @param ids 需要删除的知识库主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCcLlmKbCatByIds(String ids)
+    {
+        return ccLlmKbCatMapper.deleteCcLlmKbCatByIds(Convert.toStrArray(ids));
+    }
+
+    /**
+     * 删除知识库信息
+     * 
+     * @param id 知识库主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCcLlmKbCatById(Long id)
+    {
+        return ccLlmKbCatMapper.deleteCcLlmKbCatById(id);
+    }
+
+    @Override
+    public CcLlmKbCat selectCcLlmKbCatByCat(Long id, String cat) {
+        List<CcLlmKbCat> list = ccLlmKbCatMapper.selectCcLlmKbCatList(new CcLlmKbCat().setCat(cat));
+        if (!CollectionUtils.isEmpty(list)) {
+            if (null == id) {
+                return list.get(0);
+            } else {
+                for (CcLlmKbCat data: list) {
+                    if (data.getId() != id) {
+                        return data;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+}

+ 154 - 0
fs-service/src/main/java/com/fs/aicall/service/impl/CcLlmKbServiceImpl.java

@@ -0,0 +1,154 @@
+package com.fs.aicall.service.impl;
+
+import com.fs.aicall.domain.CcLlmKb;
+import com.fs.aicall.mapper.CcLlmKbMapper;
+import com.fs.aicall.service.ICcLlmKbService;
+import com.fs.aicall.utils.StringUtils;
+import com.fs.aicall.utils.XSSFUtils;
+import com.fs.common.core.text.Convert;
+import com.fs.common.exception.ServiceException;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.usermodel.WorkbookFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 知识库内容Service业务层处理
+ * 
+ * @author ruoyi
+ * @date 2026-01-19
+ */
+@Service
+public class CcLlmKbServiceImpl implements ICcLlmKbService
+{
+    @Autowired
+    private CcLlmKbMapper ccLlmKbMapper;
+
+    /**
+     * 查询知识库内容
+     * 
+     * @param id 知识库内容主键
+     * @return 知识库内容
+     */
+    @Override
+    public CcLlmKb selectCcLlmKbById(Long id)
+    {
+        return ccLlmKbMapper.selectCcLlmKbById(id);
+    }
+
+    /**
+     * 查询知识库内容列表
+     * 
+     * @param ccLlmKb 知识库内容
+     * @return 知识库内容
+     */
+    @Override
+    public List<CcLlmKb> selectCcLlmKbList(CcLlmKb ccLlmKb)
+    {
+        return ccLlmKbMapper.selectCcLlmKbList(ccLlmKb);
+    }
+
+    /**
+     * 新增知识库内容
+     * 
+     * @param ccLlmKb 知识库内容
+     * @return 结果
+     */
+    @Override
+    public int insertCcLlmKb(CcLlmKb ccLlmKb)
+    {
+        return ccLlmKbMapper.insertCcLlmKb(ccLlmKb);
+    }
+
+    /**
+     * 修改知识库内容
+     * 
+     * @param ccLlmKb 知识库内容
+     * @return 结果
+     */
+    @Override
+    public int updateCcLlmKb(CcLlmKb ccLlmKb)
+    {
+        return ccLlmKbMapper.updateCcLlmKb(ccLlmKb);
+    }
+
+    /**
+     * 批量删除知识库内容
+     * 
+     * @param ids 需要删除的知识库内容主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCcLlmKbByIds(String ids)
+    {
+        return ccLlmKbMapper.deleteCcLlmKbByIds(Convert.toStrArray(ids));
+    }
+
+    /**
+     * 删除知识库内容信息
+     * 
+     * @param id 知识库内容主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCcLlmKbById(Long id)
+    {
+        return ccLlmKbMapper.deleteCcLlmKbById(id);
+    }
+
+    @Override
+    public Integer selectCountByCatId(Long catId) {
+        return ccLlmKbMapper.selectCountByCatId(catId);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public String importData(MultipartFile file, Long fixedCatId) throws Exception {
+        Workbook wb = WorkbookFactory.create(file.getInputStream());
+        Sheet sheet = wb.getSheetAt(0);
+        if (sheet == null) throw new ServiceException("Excel 为空");
+        List<CcLlmKb> ccLlmKbList = new ArrayList<>();
+        // 跳过表头
+        for (int i = 1; i <= sheet.getLastRowNum(); i++) {
+            Row row = sheet.getRow(i);
+            if (row == null) continue;
+            CcLlmKb e = new CcLlmKb();
+            String title = XSSFUtils.getCellString(row.getCell(0));
+            e.setTitle(title);
+            e.setContent(XSSFUtils.getCellString(row.getCell(1)));
+            if (StringUtils.isBlank(e.getTitle())) continue;
+            e.setCatId(fixedCatId);
+            ccLlmKbList.add(e);
+        }
+        wb.close();
+        ccLlmKbMapper.deleteCcLlmKbByCatId(fixedCatId);
+        ccLlmKbMapper.insertBatch(ccLlmKbList);
+        return "导入成功";
+    }
+
+
+    @Override
+    public CcLlmKb selectCcLlmKbContentByTitle(Long id, String title, Long catId) {
+        List<CcLlmKb> list = ccLlmKbMapper.selectCcLlmKbList(new CcLlmKb().setTitle(title).setCatId(catId));
+        if (!CollectionUtils.isEmpty(list)) {
+            if (null == id) {
+                return list.get(0);
+            } else {
+                for (CcLlmKb data: list) {
+                    if (data.getId() != id) {
+                        return data;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+}

+ 168 - 0
fs-service/src/main/java/com/fs/aicall/service/impl/CcParamsServiceImpl.java

@@ -0,0 +1,168 @@
+package com.fs.aicall.service.impl;
+
+import com.fs.aicall.domain.CcParams;
+import com.fs.aicall.mapper.CcParamsMapper;
+import com.fs.aicall.service.ICcParamsService;
+import com.fs.aicall.utils.CommonUtils;
+import com.fs.aicall.utils.StringUtils;
+import com.fs.aicall.utils.http.HttpUtils;
+import com.fs.common.core.text.Convert;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.i18n.LocaleContextHolder;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * callcenter参数配置Service业务层处理
+ * 
+ * @author ruoyi
+ * @date 2025-04-21
+ */
+@Service
+@Slf4j
+public class CcParamsServiceImpl implements ICcParamsService
+{
+    @Autowired
+    private CcParamsMapper ccParamsMapper;
+
+
+    @Value("${sysconfig.hide-secret}")
+    private String sysConfigHideSecret;
+
+
+    /**
+     * 查询callcenter参数配置
+     * 
+     * @param id callcenter参数配置主键
+     * @return callcenter参数配置
+     */
+    @Override
+    public CcParams selectCcParamsById(Long id)
+    {
+        CcParams params =  ccParamsMapper.selectCcParamsById(id);
+        boolean hideSecret = Boolean.parseBoolean(sysConfigHideSecret);
+        if(params.getHideValue() == 1 && hideSecret){
+            String hideString = params.getParamValue();
+            params.setParamValue(CommonUtils.maskStringUtil(hideString));
+        }
+        return params;
+    }
+
+    /**
+     * 查询callcenter参数配置列表
+     * 
+     * @param ccParams callcenter参数配置
+     * @return callcenter参数配置
+     */
+    @Override
+    public List<CcParams> selectCcParamsList(CcParams ccParams)
+    {
+        List<CcParams> origList = ccParamsMapper.selectCcParamsList(ccParams);
+        boolean hideSecret = Boolean.parseBoolean(sysConfigHideSecret);
+        if(hideSecret) {
+            for (CcParams params : origList) {
+                if (params.getHideValue() == 1) {
+                    String hideString = params.getParamValue();
+                    params.setParamValue(CommonUtils.maskStringUtil(hideString));
+                }
+            }
+        }
+        return origList;
+    }
+
+    /**
+     * 新增callcenter参数配置
+     * 
+     * @param ccParams callcenter参数配置
+     * @return 结果
+     */
+    @Override
+    public int insertCcParams(CcParams ccParams)
+    {
+        return ccParamsMapper.insertCcParams(ccParams);
+    }
+
+    /**
+     * 修改callcenter参数配置
+     * 
+     * @param ccParams callcenter参数配置
+     * @return 结果
+     */
+    @Override
+    public int updateCcParams(CcParams ccParams)
+    {
+        // 禁止修改参数的 param_code 和 param_type,否则可能引起混乱;
+        CcParams ccParamsOld = selectCcParamsById(ccParams.getId());
+        ccParams.setParamName(ccParamsOld.getParamName());
+        ccParams.setParamCode(ccParamsOld.getParamCode());
+        ccParams.setParamType(ccParamsOld.getParamType());
+
+        boolean hideSecret = Boolean.parseBoolean(sysConfigHideSecret);
+        boolean containsMaskStr =  ccParams.getParamValue().contains("**");
+        if(!hideSecret || !containsMaskStr) {
+            return ccParamsMapper.updateCcParams(ccParams);
+        }
+        return 1;
+    }
+
+    /**
+     * 批量删除callcenter参数配置
+     * 
+     * @param ids 需要删除的callcenter参数配置主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCcParamsByIds(String ids)
+    {
+        return ccParamsMapper.deleteCcParamsByIds(Convert.toStrArray(ids));
+    }
+
+    /**
+     * 删除callcenter参数配置信息
+     * 
+     * @param id callcenter参数配置主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCcParamsById(Long id)
+    {
+        return ccParamsMapper.deleteCcParamsById(id);
+    }
+
+    @Override
+    public String getParamValueByCode(String paramCode, String defaultValue) {
+        Locale locale = LocaleContextHolder.getLocale();
+        // 先找对应语言包的配置,没有的话再找通用配置
+        List<CcParams> list = ccParamsMapper.selectCcParamsList(new CcParams().setParamCode(paramCode + "_" + locale.toString()));
+        if (list.size() > 0) {
+            return list.get(0).getParamValue();
+        } else {
+            list = ccParamsMapper.selectCcParamsList(new CcParams().setParamCode(paramCode));
+            if (list.size() > 0) {
+                return list.get(0).getParamValue();
+            }
+        }
+        return defaultValue;
+    }
+
+    @Override
+    public void updateParamsValue(String paramCode, String paramValue) {
+        ccParamsMapper.updateParamsValue(paramCode, paramValue);
+    }
+
+    @Override
+    public String reloadParams() {
+        // Access the 'reloadParams' webapi interface to make the parameters take effect;
+        String serverPort = getParamValueByCode("call-center-server-port", "");
+        if(!StringUtils.isEmpty(serverPort)){
+            String reloadParamsUrl = String.format("http://127.0.0.1:%s/call-center/reloadParams", serverPort);
+            String response = HttpUtils.sendGet(reloadParamsUrl);
+            return response;
+        }
+        return "";
+    }
+}

+ 170 - 0
fs-service/src/main/java/com/fs/aicall/service/impl/CompanyBindAiModelServiceImpl.java

@@ -0,0 +1,170 @@
+package com.fs.aicall.service.impl;
+
+import com.fs.aicall.domain.CompanyBindAiModel;
+import com.fs.aicall.mapper.CompanyBindAiModelMapper;
+import com.fs.aicall.service.ICompanyBindAiModelService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 销售公司与AI模型绑定表Service实现类
+ * 
+ * @author ruoyi
+ * @date 2026-03-20
+ */
+@Service
+public class CompanyBindAiModelServiceImpl implements ICompanyBindAiModelService
+{
+    @Autowired
+    private CompanyBindAiModelMapper companyBindAiModelMapper;
+
+    /**
+     * 查询销售公司与AI模型绑定表
+     * 
+     * @param id 销售公司与AI模型绑定表主键
+     * @return 销售公司与AI模型绑定表
+     */
+    @Override
+    public CompanyBindAiModel selectCompanyBindAiModelById(Long id)
+    {
+        return companyBindAiModelMapper.selectCompanyBindAiModelById(id);
+    }
+
+    /**
+     * 查询销售公司与AI模型绑定表列表
+     * 
+     * @param companyBindAiModel 销售公司与AI模型绑定表
+     * @return 销售公司与AI模型绑定表集合
+     */
+    @Override
+    public List<CompanyBindAiModel> selectCompanyBindAiModelList(CompanyBindAiModel companyBindAiModel)
+    {
+        return companyBindAiModelMapper.selectCompanyBindAiModelList(companyBindAiModel);
+    }
+
+    /**
+     * 新增销售公司与AI模型绑定表
+     * 
+     * @param companyBindAiModel 销售公司与AI模型绑定表
+     * @return 结果
+     */
+    @Override
+    public int insertCompanyBindAiModel(CompanyBindAiModel companyBindAiModel)
+    {
+        companyBindAiModel.setCreateTime(new Date());
+        companyBindAiModel.setUpdateTime(new Date());
+        return companyBindAiModelMapper.insertCompanyBindAiModel(companyBindAiModel);
+    }
+
+    /**
+     * 修改销售公司与AI模型绑定表
+     * 
+     * @param companyBindAiModel 销售公司与AI模型绑定表
+     * @return 结果
+     */
+    @Override
+    public int updateCompanyBindAiModel(CompanyBindAiModel companyBindAiModel)
+    {
+        companyBindAiModel.setUpdateTime(new Date());
+        return companyBindAiModelMapper.updateCompanyBindAiModel(companyBindAiModel);
+    }
+
+    /**
+     * 删除销售公司与AI模型绑定表
+     * 
+     * @param id 销售公司与AI模型绑定表主键
+     * @return 结果
+     */
+    @Override
+    public int deleteCompanyBindAiModelById(Long id)
+    {
+        return companyBindAiModelMapper.deleteCompanyBindAiModelById(id);
+    }
+
+    /**
+     * 批量删除销售公司与AI模型绑定表
+     * 
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    @Override
+    public int deleteCompanyBindAiModelByIds(Long[] ids)
+    {
+        return companyBindAiModelMapper.deleteCompanyBindAiModelByIds(ids);
+    }
+
+    /**
+     * 根据公司ID查询绑定的模型ID列表
+     * 
+     * @param companyId 公司ID
+     * @return 模型ID列表
+     */
+    @Override
+    public List<Long> selectModelIdsByCompanyId(Long companyId)
+    {
+        return companyBindAiModelMapper.selectModelIdsByCompanyId(companyId);
+    }
+
+    /**
+     * 根据模型ID删除绑定关系
+     * 
+     * @param modelId 模型ID
+     * @return 结果
+     */
+    @Override
+    public int deleteCompanyBindAiModelByModelId(Long modelId)
+    {
+        return companyBindAiModelMapper.deleteCompanyBindAiModelByModelId(modelId);
+    }
+
+    /**
+     * 批量插入绑定关系
+     * 
+     * @param companyId 公司ID
+     * @param modelIds 模型ID列表
+     * @return 结果
+     */
+    @Override
+    public int batchInsertCompanyBindAiModel(Long companyId, List<Long> modelIds)
+    {
+        return companyBindAiModelMapper.batchInsertCompanyBindAiModel(companyId, modelIds);
+    }
+
+    /**
+     * 为模型添加公司绑定
+     * 
+     * @param modelId 模型ID
+     * @param companyId 公司ID
+     * @return 结果
+     */
+    @Override
+    public int bindCompanyToModel(Long modelId, Long companyId)
+    {
+        CompanyBindAiModel companyBindAiModel = new CompanyBindAiModel();
+        companyBindAiModel.setModelId(modelId);
+        companyBindAiModel.setCompanyId(companyId);
+        companyBindAiModel.setCreateTime(new Date());
+        companyBindAiModel.setUpdateTime(new Date());
+        return companyBindAiModelMapper.insertCompanyBindAiModel(companyBindAiModel);
+    }
+
+    /**
+     * 为模型批量添加公司绑定
+     * 
+     * @param modelId 模型ID
+     * @param companyIds 公司ID列表
+     * @return 结果
+     */
+    @Override
+    public int batchBindCompaniesToModel(Long modelId, List<Long> companyIds)
+    {
+        int result = 0;
+        for (Long companyId : companyIds) {
+            result += bindCompanyToModel(modelId, companyId);
+        }
+        return result;
+    }
+}

+ 105 - 0
fs-service/src/main/java/com/fs/aicall/utils/CommonUtils.java

@@ -0,0 +1,105 @@
+package com.fs.aicall.utils;
+
+import java.util.List;
+
+public class CommonUtils {
+    public static String getStackTraceString(StackTraceElement[] stackTraceElements){
+        StringBuilder stringBuilder = new StringBuilder();
+        for (int i = 0; i < stackTraceElements.length; i++) {
+            stringBuilder.append("ClassName:");
+            stringBuilder.append(stackTraceElements[i].getClassName());
+            stringBuilder.append("\n FileName:");
+            stringBuilder.append(stackTraceElements[i].getFileName());
+            stringBuilder.append("\n LineNumber:");
+            stringBuilder.append(stackTraceElements[i].getLineNumber());
+            stringBuilder.append("\n MethodName:");
+            stringBuilder.append(stackTraceElements[i].getMethodName());
+        }
+        return stringBuilder.toString();
+    }
+
+    public static String ListToString(List<?> objectList) {
+        if(objectList.size() == 0) {
+            return "";
+        }
+        return ListToString(objectList, true);
+    }
+
+    /**
+     * ListToString,是否使用逗号分隔符
+     ***/
+    public static String ListToString(List<?> objectList, boolean useSpe) {
+        StringBuilder sb = new StringBuilder("");
+        for (Object ele : objectList) {
+            sb.append(ele);
+            if (useSpe) {
+                sb.append(",");
+            }
+        }
+        String result = sb.toString();
+        if (useSpe) {
+            result = result.substring(0, result.length() - 1);
+        }
+        return result;
+    }
+
+    /**
+     * ListToString, 使用分隔符
+     ***/
+    public static String ListToString(List<?> objectList, char delimiter) {
+        StringBuilder sb = new StringBuilder("");
+        for (Object ele : objectList) {
+            sb.append(ele);
+            sb.append(delimiter);
+        }
+        String result = sb.toString();
+        result = result.substring(0, result.length() - 1);
+        return result;
+    }
+
+    /**
+     *  隐藏敏感字符串
+     * @param input
+     * @return
+     */
+    public static String maskStringUtil(String input) {
+        if (input == null || input.isEmpty()) {
+            return input;
+        }
+
+        int length = input.length();
+        if (length <= 10) {
+            // 场景1:长度≤10,隐藏第4-8位(索引3-7)
+            if (length < 10) {
+                return repeatString("*", length); // 不足3位直接返回
+            }
+            int startIdx = 3;
+            int endIdx = Math.min(8, length); // 防止越界
+            String stars = repeatString("*", endIdx - startIdx);
+            return input.substring(0, startIdx) + stars + input.substring(endIdx);
+        } else {
+            // 场景2:长度>10,仅保留前3位和后3位
+            String prefix = input.substring(0, 3);
+            String suffix = input.substring(length - 3);
+            String stars = repeatString("*", length - 6);
+            return prefix + stars + suffix;
+        }
+    }
+
+    /**
+     * 手动实现字符串重复方法(兼容Java 8以下)
+     * @param str
+     * @param times
+     * @return
+     */
+    private static String repeatString(String str, int times) {
+        if (times <= 0) {
+            return "";
+        }
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < times; i++) {
+            sb.append(str);
+        }
+        return sb.toString();
+    }
+}

+ 672 - 0
fs-service/src/main/java/com/fs/aicall/utils/StringUtils.java

@@ -0,0 +1,672 @@
+package com.fs.aicall.utils;
+
+import com.fs.common.constant.Constants;
+import com.fs.common.core.text.StrFormatter;
+import org.springframework.util.AntPathMatcher;
+
+import java.util.*;
+
+/**
+ * 字符串工具类
+ * 
+ * @author ruoyi
+ */
+public class StringUtils extends org.apache.commons.lang3.StringUtils
+{
+    /** 空字符串 */
+    private static final String NULLSTR = "";
+
+    /** 下划线 */
+    private static final char SEPARATOR = '_';
+
+    /** 星号 */
+    private static final char ASTERISK = '*';
+
+    /**
+     * 获取参数不为空值
+     * 
+     * @param value defaultValue 要判断的value
+     * @return value 返回值
+     */
+    public static <T> T nvl(T value, T defaultValue)
+    {
+        return value != null ? value : defaultValue;
+    }
+
+    /**
+     * * 判断一个Collection是否为空, 包含List,Set,Queue
+     * 
+     * @param coll 要判断的Collection
+     * @return true:为空 false:非空
+     */
+    public static boolean isEmpty(Collection<?> coll)
+    {
+        return isNull(coll) || coll.isEmpty();
+    }
+
+    /**
+     * * 判断一个Collection是否非空,包含List,Set,Queue
+     * 
+     * @param coll 要判断的Collection
+     * @return true:非空 false:空
+     */
+    public static boolean isNotEmpty(Collection<?> coll)
+    {
+        return !isEmpty(coll);
+    }
+
+    /**
+     * * 判断一个对象数组是否为空
+     * 
+     * @param objects 要判断的对象数组
+     ** @return true:为空 false:非空
+     */
+    public static boolean isEmpty(Object[] objects)
+    {
+        return isNull(objects) || (objects.length == 0);
+    }
+
+    /**
+     * * 判断一个对象数组是否非空
+     * 
+     * @param objects 要判断的对象数组
+     * @return true:非空 false:空
+     */
+    public static boolean isNotEmpty(Object[] objects)
+    {
+        return !isEmpty(objects);
+    }
+
+    /**
+     * * 判断一个Map是否为空
+     * 
+     * @param map 要判断的Map
+     * @return true:为空 false:非空
+     */
+    public static boolean isEmpty(Map<?, ?> map)
+    {
+        return isNull(map) || map.isEmpty();
+    }
+
+    /**
+     * * 判断一个Map是否为空
+     * 
+     * @param map 要判断的Map
+     * @return true:非空 false:空
+     */
+    public static boolean isNotEmpty(Map<?, ?> map)
+    {
+        return !isEmpty(map);
+    }
+
+    /**
+     * * 判断一个字符串是否为空串
+     * 
+     * @param str String
+     * @return true:为空 false:非空
+     */
+    public static boolean isEmpty(String str)
+    {
+        return isNull(str) || NULLSTR.equals(str.trim());
+    }
+
+    /**
+     * * 判断一个字符串是否为非空串
+     * 
+     * @param str String
+     * @return true:非空串 false:空串
+     */
+    public static boolean isNotEmpty(String str)
+    {
+        return !isEmpty(str);
+    }
+
+    /**
+     * * 判断一个对象是否为空
+     * 
+     * @param object Object
+     * @return true:为空 false:非空
+     */
+    public static boolean isNull(Object object)
+    {
+        return object == null;
+    }
+
+    /**
+     * * 判断一个对象是否非空
+     * 
+     * @param object Object
+     * @return true:非空 false:空
+     */
+    public static boolean isNotNull(Object object)
+    {
+        return !isNull(object);
+    }
+
+    /**
+     * * 判断一个对象是否是数组类型(Java基本型别的数组)
+     * 
+     * @param object 对象
+     * @return true:是数组 false:不是数组
+     */
+    public static boolean isArray(Object object)
+    {
+        return isNotNull(object) && object.getClass().isArray();
+    }
+
+    /**
+     * 去空格
+     */
+    public static String trim(String str)
+    {
+        return (str == null ? "" : str.trim());
+    }
+
+    /**
+     * 替换指定字符串的指定区间内字符为"*"
+     *
+     * @param str 字符串
+     * @param startInclude 开始位置(包含)
+     * @param endExclude 结束位置(不包含)
+     * @return 替换后的字符串
+     */
+    public static String hide(CharSequence str, int startInclude, int endExclude)
+    {
+        if (isEmpty(str))
+        {
+            return NULLSTR;
+        }
+        final int strLength = str.length();
+        if (startInclude > strLength)
+        {
+            return NULLSTR;
+        }
+        if (endExclude > strLength)
+        {
+            endExclude = strLength;
+        }
+        if (startInclude > endExclude)
+        {
+            // 如果起始位置大于结束位置,不替换
+            return NULLSTR;
+        }
+        final char[] chars = new char[strLength];
+        for (int i = 0; i < strLength; i++)
+        {
+            if (i >= startInclude && i < endExclude)
+            {
+                chars[i] = ASTERISK;
+            }
+            else
+            {
+                chars[i] = str.charAt(i);
+            }
+        }
+        return new String(chars);
+    }
+
+    /**
+     * 截取字符串
+     * 
+     * @param str 字符串
+     * @param start 开始
+     * @return 结果
+     */
+    public static String substring(final String str, int start)
+    {
+        if (str == null)
+        {
+            return NULLSTR;
+        }
+
+        if (start < 0)
+        {
+            start = str.length() + start;
+        }
+
+        if (start < 0)
+        {
+            start = 0;
+        }
+        if (start > str.length())
+        {
+            return NULLSTR;
+        }
+
+        return str.substring(start);
+    }
+
+    /**
+     * 截取字符串
+     * 
+     * @param str 字符串
+     * @param start 开始
+     * @param end 结束
+     * @return 结果
+     */
+    public static String substring(final String str, int start, int end)
+    {
+        if (str == null)
+        {
+            return NULLSTR;
+        }
+
+        if (end < 0)
+        {
+            end = str.length() + end;
+        }
+        if (start < 0)
+        {
+            start = str.length() + start;
+        }
+
+        if (end > str.length())
+        {
+            end = str.length();
+        }
+
+        if (start > end)
+        {
+            return NULLSTR;
+        }
+
+        if (start < 0)
+        {
+            start = 0;
+        }
+        if (end < 0)
+        {
+            end = 0;
+        }
+
+        return str.substring(start, end);
+    }
+
+    /**
+     * 格式化文本, {} 表示占位符<br>
+     * 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
+     * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
+     * 例:<br>
+     * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br>
+     * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br>
+     * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
+     * 
+     * @param template 文本模板,被替换的部分用 {} 表示
+     * @param params 参数值
+     * @return 格式化后的文本
+     */
+    public static String format(String template, Object... params)
+    {
+        if (isEmpty(params) || isEmpty(template))
+        {
+            return template;
+        }
+        return StrFormatter.format(template, params);
+    }
+
+    /**
+     * 是否为http(s)://开头
+     * 
+     * @param link 链接
+     * @return 结果
+     */
+    public static boolean ishttp(String link)
+    {
+        return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS);
+    }
+
+    /**
+     * 字符串转set
+     * 
+     * @param str 字符串
+     * @param sep 分隔符
+     * @return set集合
+     */
+    public static final Set<String> str2Set(String str, String sep)
+    {
+        return new HashSet<String>(str2List(str, sep, true, false));
+    }
+
+    /**
+     * 字符串转list
+     * 
+     * @param str 字符串
+     * @param sep 分隔符
+     * @param filterBlank 过滤纯空白
+     * @param trim 去掉首尾空白
+     * @return list集合
+     */
+    public static final List<String> str2List(String str, String sep, boolean filterBlank, boolean trim)
+    {
+        List<String> list = new ArrayList<String>();
+        if (StringUtils.isEmpty(str))
+        {
+            return list;
+        }
+
+        // 过滤空白字符串
+        if (filterBlank && StringUtils.isBlank(str))
+        {
+            return list;
+        }
+        String[] split = str.split(sep);
+        for (String string : split)
+        {
+            if (filterBlank && StringUtils.isBlank(string))
+            {
+                continue;
+            }
+            if (trim)
+            {
+                string = string.trim();
+            }
+            list.add(string);
+        }
+
+        return list;
+    }
+
+    /**
+     * 判断给定的collection列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value
+     *
+     * @param collection 给定的集合
+     * @param array 给定的数组
+     * @return boolean 结果
+     */
+    public static boolean containsAny(Collection<String> collection, String... array)
+    {
+        if (isEmpty(collection) || isEmpty(array))
+        {
+            return false;
+        }
+        else
+        {
+            for (String str : array)
+            {
+                if (collection.contains(str))
+                {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    /**
+     * 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写
+     *
+     * @param cs 指定字符串
+     * @param searchCharSequences 需要检查的字符串数组
+     * @return 是否包含任意一个字符串
+     */
+    public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences)
+    {
+        if (isEmpty(cs) || isEmpty(searchCharSequences))
+        {
+            return false;
+        }
+        for (CharSequence testStr : searchCharSequences)
+        {
+            if (containsIgnoreCase(cs, testStr))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 驼峰转下划线命名
+     */
+    public static String toUnderScoreCase(String str)
+    {
+        if (str == null)
+        {
+            return null;
+        }
+        StringBuilder sb = new StringBuilder();
+        // 前置字符是否大写
+        boolean preCharIsUpperCase = true;
+        // 当前字符是否大写
+        boolean curreCharIsUpperCase = true;
+        // 下一字符是否大写
+        boolean nexteCharIsUpperCase = true;
+        for (int i = 0; i < str.length(); i++)
+        {
+            char c = str.charAt(i);
+            if (i > 0)
+            {
+                preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1));
+            }
+            else
+            {
+                preCharIsUpperCase = false;
+            }
+
+            curreCharIsUpperCase = Character.isUpperCase(c);
+
+            if (i < (str.length() - 1))
+            {
+                nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1));
+            }
+
+            if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase)
+            {
+                sb.append(SEPARATOR);
+            }
+            else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase)
+            {
+                sb.append(SEPARATOR);
+            }
+            sb.append(Character.toLowerCase(c));
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * 是否包含字符串
+     * 
+     * @param str 验证字符串
+     * @param strs 字符串组
+     * @return 包含返回true
+     */
+    public static boolean inStringIgnoreCase(String str, String... strs)
+    {
+        if (str != null && strs != null)
+        {
+            for (String s : strs)
+            {
+                if (str.equalsIgnoreCase(trim(s)))
+                {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 删除最后一个字符串
+     *
+     * @param str 输入字符串
+     * @param spit 以什么类型结尾的
+     * @return 截取后的字符串
+     */
+    public static String lastStringDel(String str, String spit)
+    {
+        if (!StringUtils.isEmpty(str) && str.endsWith(spit))
+        {
+            return str.subSequence(0, str.length() - 1).toString();
+        }
+        return str;
+    }
+
+    /**
+     * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld
+     * 
+     * @param name 转换前的下划线大写方式命名的字符串
+     * @return 转换后的驼峰式命名的字符串
+     */
+    public static String convertToCamelCase(String name)
+    {
+        StringBuilder result = new StringBuilder();
+        // 快速检查
+        if (name == null || name.isEmpty())
+        {
+            // 没必要转换
+            return "";
+        }
+        else if (!name.contains("_"))
+        {
+            // 不含下划线,仅将首字母大写
+            return name.substring(0, 1).toUpperCase() + name.substring(1);
+        }
+        // 用下划线将原始字符串分割
+        String[] camels = name.split("_");
+        for (String camel : camels)
+        {
+            // 跳过原始字符串中开头、结尾的下换线或双重下划线
+            if (camel.isEmpty())
+            {
+                continue;
+            }
+            // 首字母大写
+            result.append(camel.substring(0, 1).toUpperCase());
+            result.append(camel.substring(1).toLowerCase());
+        }
+        return result.toString();
+    }
+
+    /**
+     * 驼峰式命名法
+     * 例如:user_name->userName
+     */
+    public static String toCamelCase(String s)
+    {
+        if (s == null)
+        {
+            return null;
+        }
+        if (s.indexOf(SEPARATOR) == -1)
+        {
+            return s;
+        }
+        s = s.toLowerCase();
+        StringBuilder sb = new StringBuilder(s.length());
+        boolean upperCase = false;
+        for (int i = 0; i < s.length(); i++)
+        {
+            char c = s.charAt(i);
+
+            if (c == SEPARATOR)
+            {
+                upperCase = true;
+            }
+            else if (upperCase)
+            {
+                sb.append(Character.toUpperCase(c));
+                upperCase = false;
+            }
+            else
+            {
+                sb.append(c);
+            }
+        }
+        return sb.toString();
+    }
+
+    /**
+     * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串
+     * 
+     * @param str 指定字符串
+     * @param strs 需要检查的字符串数组
+     * @return 是否匹配
+     */
+    public static boolean matches(String str, List<String> strs)
+    {
+        if (isEmpty(str) || isEmpty(strs))
+        {
+            return false;
+        }
+        for (String pattern : strs)
+        {
+            if (isMatch(pattern, str))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 判断url是否与规则配置: 
+     * ? 表示单个字符; 
+     * * 表示一层路径内的任意字符串,不可跨层级; 
+     * ** 表示任意层路径;
+     * 
+     * @param pattern 匹配规则
+     * @param url 需要匹配的url
+     * @return
+     */
+    public static boolean isMatch(String pattern, String url)
+    {
+        AntPathMatcher matcher = new AntPathMatcher();
+        return matcher.match(pattern, url);
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <T> T cast(Object obj)
+    {
+        return (T) obj;
+    }
+
+    /**
+     * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。
+     * 
+     * @param num 数字对象
+     * @param size 字符串指定长度
+     * @return 返回数字的字符串格式,该字符串为指定长度。
+     */
+    public static final String padl(final Number num, final int size)
+    {
+        return padl(num.toString(), size, '0');
+    }
+
+    /**
+     * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。
+     * 
+     * @param s 原始字符串
+     * @param size 字符串指定长度
+     * @param c 用于补齐的字符
+     * @return 返回指定长度的字符串,由原字符串左补齐或截取得到。
+     */
+    public static final String padl(final String s, final int size, final char c)
+    {
+        final StringBuilder sb = new StringBuilder(size);
+        if (s != null)
+        {
+            final int len = s.length();
+            if (s.length() <= size)
+            {
+                for (int i = size - len; i > 0; i--)
+                {
+                    sb.append(c);
+                }
+                sb.append(s);
+            }
+            else
+            {
+                return s.substring(len - size, len);
+            }
+        }
+        else
+        {
+            for (int i = size; i > 0; i--)
+            {
+                sb.append(c);
+            }
+        }
+        return sb.toString();
+    }
+}

+ 12 - 0
fs-service/src/main/java/com/fs/aicall/utils/XSSFUtils.java

@@ -0,0 +1,12 @@
+package com.fs.aicall.utils;
+
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellType;
+
+public class XSSFUtils {
+    public static String getCellString(Cell cell) {
+        if (cell == null) return "";
+        cell.setCellType(CellType.STRING);
+        return cell.getStringCellValue() == null ? "" : cell.getStringCellValue().trim();
+    }
+}

+ 276 - 0
fs-service/src/main/java/com/fs/aicall/utils/http/HttpUtils.java

@@ -0,0 +1,276 @@
+package com.fs.aicall.utils.http;
+
+import com.fs.aicall.utils.StringUtils;
+import com.fs.common.constant.Constants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.net.ssl.*;
+import java.io.*;
+import java.net.ConnectException;
+import java.net.SocketTimeoutException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.charset.StandardCharsets;
+import java.security.cert.X509Certificate;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 通用http发送方法
+ * 
+ * @author ruoyi
+ */
+public class HttpUtils
+{
+    private static final Logger log = LoggerFactory.getLogger(HttpUtils.class);
+
+    /**
+     * 向指定 URL 发送GET方法的请求
+     *
+     * @param url 发送请求的 URL
+     * @return 所代表远程资源的响应结果
+     */
+    public static String sendGet(String url)
+    {
+        return sendGet(url, StringUtils.EMPTY);
+    }
+
+    /**
+     * 向指定 URL 发送GET方法的请求
+     *
+     * @param url 发送请求的 URL
+     * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
+     * @return 所代表远程资源的响应结果
+     */
+    public static String sendGet(String url, String param)
+    {
+        return sendGet(url, param, Constants.UTF8);
+    }
+
+    public static String sendGet(String url, String param, String contentType) {
+        return sendGet(url, param, contentType, new HashMap<>());
+    }
+
+    /**
+     * 向指定 URL 发送GET方法的请求
+     *
+     * @param url 发送请求的 URL
+     * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
+     * @param contentType 编码类型
+     * @return 所代表远程资源的响应结果
+     */
+    public static String sendGet(String url, String param, String contentType, Map<String, String> headers )
+    {
+        StringBuilder result = new StringBuilder();
+        BufferedReader in = null;
+        try
+        {
+            String urlNameString = StringUtils.isNotBlank(param) ? url + "?" + param : url;
+            log.info("sendGet - {}", urlNameString);
+            URL realUrl = new URL(urlNameString);
+            URLConnection connection = realUrl.openConnection();
+            connection.setRequestProperty("accept", "*/*");
+            connection.setRequestProperty("connection", "Keep-Alive");
+            connection.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
+            /* ===== 动态头 ===== */
+            if (headers != null) {
+                headers.forEach(connection::setRequestProperty);
+            }
+            connection.connect();
+            in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType));
+            String line;
+            while ((line = in.readLine()) != null)
+            {
+                result.append(line);
+            }
+            log.info("recv - {}", result);
+        }
+        catch (ConnectException e)
+        {
+            log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e);
+        }
+        catch (SocketTimeoutException e)
+        {
+            log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e);
+        }
+        catch (IOException e)
+        {
+            log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e);
+        }
+        catch (Exception e)
+        {
+            log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e);
+        }
+        finally
+        {
+            try
+            {
+                if (in != null)
+                {
+                    in.close();
+                }
+            }
+            catch (Exception ex)
+            {
+                log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
+            }
+        }
+        return result.toString();
+    }
+
+    /**
+     * 向指定 URL 发送POST方法的请求
+     *
+     * @param url 发送请求的 URL
+     * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
+     * @return 所代表远程资源的响应结果
+     */
+    public static String sendPost(String url, String param)
+    {
+        PrintWriter out = null;
+        BufferedReader in = null;
+        StringBuilder result = new StringBuilder();
+        try
+        {
+            log.info("sendPost - {}", url);
+            URL realUrl = new URL(url);
+            URLConnection conn = realUrl.openConnection();
+            conn.setRequestProperty("accept", "*/*");
+            conn.setRequestProperty("connection", "Keep-Alive");
+            conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
+            conn.setRequestProperty("Accept-Charset", "utf-8");
+            conn.setRequestProperty("contentType", "utf-8");
+            conn.setDoOutput(true);
+            conn.setDoInput(true);
+            out = new PrintWriter(conn.getOutputStream());
+            out.print(param);
+            out.flush();
+            in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8));
+            String line;
+            while ((line = in.readLine()) != null)
+            {
+                result.append(line);
+            }
+            log.info("recv - {}", result);
+        }
+        catch (ConnectException e)
+        {
+            log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e);
+        }
+        catch (SocketTimeoutException e)
+        {
+            log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e);
+        }
+        catch (IOException e)
+        {
+            log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e);
+        }
+        catch (Exception e)
+        {
+            log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e);
+        }
+        finally
+        {
+            try
+            {
+                if (out != null)
+                {
+                    out.close();
+                }
+                if (in != null)
+                {
+                    in.close();
+                }
+            }
+            catch (IOException ex)
+            {
+                log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
+            }
+        }
+        return result.toString();
+    }
+
+    public static String sendSSLPost(String url, String param)
+    {
+        StringBuilder result = new StringBuilder();
+        String urlNameString = url + "?" + param;
+        try
+        {
+            log.info("sendSSLPost - {}", urlNameString);
+            SSLContext sc = SSLContext.getInstance("SSL");
+            sc.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom());
+            URL console = new URL(urlNameString);
+            HttpsURLConnection conn = (HttpsURLConnection) console.openConnection();
+            conn.setRequestProperty("accept", "*/*");
+            conn.setRequestProperty("connection", "Keep-Alive");
+            conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
+            conn.setRequestProperty("Accept-Charset", "utf-8");
+            conn.setRequestProperty("contentType", "utf-8");
+            conn.setDoOutput(true);
+            conn.setDoInput(true);
+
+            conn.setSSLSocketFactory(sc.getSocketFactory());
+            conn.setHostnameVerifier(new TrustAnyHostnameVerifier());
+            conn.connect();
+            InputStream is = conn.getInputStream();
+            BufferedReader br = new BufferedReader(new InputStreamReader(is));
+            String ret = "";
+            while ((ret = br.readLine()) != null)
+            {
+                if (ret != null && !ret.trim().equals(""))
+                {
+                    result.append(new String(ret.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8));
+                }
+            }
+            log.info("recv - {}", result);
+            conn.disconnect();
+            br.close();
+        }
+        catch (ConnectException e)
+        {
+            log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e);
+        }
+        catch (SocketTimeoutException e)
+        {
+            log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e);
+        }
+        catch (IOException e)
+        {
+            log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e);
+        }
+        catch (Exception e)
+        {
+            log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e);
+        }
+        return result.toString();
+    }
+
+    private static class TrustAnyTrustManager implements X509TrustManager
+    {
+        @Override
+        public void checkClientTrusted(X509Certificate[] chain, String authType)
+        {
+        }
+
+        @Override
+        public void checkServerTrusted(X509Certificate[] chain, String authType)
+        {
+        }
+
+        @Override
+        public X509Certificate[] getAcceptedIssuers()
+        {
+            return new X509Certificate[] {};
+        }
+    }
+
+    private static class TrustAnyHostnameVerifier implements HostnameVerifier
+    {
+        @Override
+        public boolean verify(String hostname, SSLSession session)
+        {
+            return true;
+        }
+    }
+}

+ 9 - 0
fs-service/src/main/resources/application-dev.yml

@@ -253,3 +253,12 @@ wx_miniapp_temp:
     pay_order_temp_id:
     inquiry_temp_id:
 
+sysconfig:
+    # 包含哪些关键词的配置文件参数,将会被mask打码隐藏
+    hidden-key-list: api/app/token/key/secret/access
+    # 是否开启敏感参数隐藏功能
+    hide-secret: true
+    # 系统版本号
+    sysVersion: v20260217
+    # 是否开启登陆时选择业务组
+    show-dynamic-groupid: true

+ 178 - 0
fs-service/src/main/resources/mapper/aicall/CcCallTaskMapper.xml

@@ -0,0 +1,178 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.aicall.mapper.CcCallTaskMapper">
+    
+    <resultMap type="CcCallTask" id="CcCallTaskResult">
+        <result property="batchId"    column="batch_id"    />
+        <result property="groupId"    column="group_id"    />
+        <result property="batchName"    column="batch_name"    />
+        <result property="ifcall"    column="ifcall"    />
+        <result property="rate"    column="rate"    />
+        <result property="threadNum"    column="thread_num"    />
+        <result property="createtime"    column="createtime"    />
+        <result property="executing"    column="executing"    />
+        <result property="stopTime"    column="stop_time"    />
+        <result property="userid"    column="userid"    />
+        <result property="gatewayId"    column="gateway_id"    />
+        <result property="taskType"    column="task_type"    />
+        <result property="voiceCode"    column="voice_code"    />
+        <result property="voiceSource"    column="voice_source"    />
+        <result property="avgRingTimeLen"    column="avg_ring_time_len"    />
+        <result property="avgCallTalkTimeLen"    column="avg_call_talk_time_len"    />
+        <result property="avgCallEndProcessTimeLen"    column="avg_call_end_process_time_len"    />
+        <result property="callNodeNo"    column="call_node_no"    />
+        <result property="llmAccountId"    column="llm_account_id"    />
+        <result property="playTimes"    column="play_times"    />
+        <result property="asrProvider"    column="asr_provider"    />
+        <result property="aiTransferType"    column="ai_transfer_type"    />
+        <result property="aiTransferData"    column="ai_transfer_data"    />
+        <result property="autoStop"    column="auto_stop"    />
+        <result property="ivrId"    column="ivr_id"    />
+    </resultMap>
+
+    <sql id="selectCcCallTaskVo">
+        select batch_id, group_id, batch_name, ifcall, rate, thread_num, createtime, executing, stop_time, userid, task_type, gateway_id, voice_code, voice_source, avg_ring_time_len, avg_call_talk_time_len, avg_call_end_process_time_len, call_node_no, llm_account_id, play_times, asr_provider, ai_transfer_type, ai_transfer_data, auto_stop, ivr_id from cc_call_task
+    </sql>
+
+    <select id="selectCcCallTaskList" parameterType="CcCallTask" resultMap="CcCallTaskResult">
+        <include refid="selectCcCallTaskVo"/>
+        <where>
+            <if test="batchId != null "> and batch_id = #{batchId}</if>
+            <if test="groupId != null  and groupId != ''"> and group_id = #{groupId}</if>
+            <if test="batchName != null  and batchName != ''"> and batch_name like concat('%', #{batchName}, '%')</if>
+            <if test="ifcall != null "> and ifcall = #{ifcall}</if>
+            <if test="taskType != null "> and task_type = #{taskType}</if>
+            <if test="gatewayId != null "> and gateway_id = #{gatewayId}</if>
+            <if test="voiceCode != null and voiceCode != ''"> and voice_code = #{voiceCode}</if>
+            <if test="voiceSource != null and voiceSource != ''"> and voice_source = #{voiceSource}</if>
+            <if test="llmAccountId != null"> and llm_account_id = #{llmAccountId}</if>
+            <if test="params.createTimeStart != null and params.createTimeStart != ''"><!-- 开始时间检索 -->
+                AND createtime &gt;= #{params.createTimeStart}
+            </if>
+            <if test="params.createTimeEnd != null and params.createTimeEnd != ''"><!-- 结束时间检索 -->
+                AND createtime &lt;= #{params.createTimeEnd}
+            </if>
+            <if test="asrProvider != null and asrProvider != ''" > and asr_provider = #{asrProvider}</if>
+            <if test="aiTransferType != null and aiTransferType != ''" > and ai_transfer_type = #{aiTransferType}</if>
+            <if test="aiTransferData != null and aiTransferData != ''" > and ai_transfer_data = #{aiTransferData}</if>
+        </where>
+        order by batch_id desc
+    </select>
+    
+    <select id="selectCcCallTaskByBatchId" parameterType="Long" resultMap="CcCallTaskResult">
+        <include refid="selectCcCallTaskVo"/>
+        where batch_id = #{batchId}
+    </select>
+
+    <insert id="insertCcCallTask" parameterType="CcCallTask" useGeneratedKeys="true" keyProperty="batchId">
+        insert into cc_call_task
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="groupId != null and groupId != ''">group_id,</if>
+            <if test="batchName != null and batchName != ''">batch_name,</if>
+            <if test="ifcall != null">ifcall,</if>
+            <if test="rate != null">rate,</if>
+            <if test="threadNum != null">thread_num,</if>
+            <if test="createtime != null">createtime,</if>
+            <if test="executing != null">executing,</if>
+            <if test="stopTime != null">stop_time,</if>
+            <if test="userid != null and userid != ''">userid,</if>
+            <if test="taskType != null">task_type,</if>
+            <if test="gatewayId != null">gateway_id,</if>
+            <if test="voiceCode != null and voiceCode != ''">voice_code,</if>
+            <if test="voiceSource != null and voiceSource != ''">voice_source,</if>
+            <if test="avgRingTimeLen != null">avg_ring_time_len,</if>
+            <if test="avgCallTalkTimeLen != null">avg_call_talk_time_len,</if>
+            <if test="avgCallEndProcessTimeLen != null">avg_call_end_process_time_len,</if>
+            <if test="callNodeNo != null">call_node_no,</if>
+            <if test="llmAccountId != null">llm_account_id,</if>
+            <if test="playTimes != null">play_times,</if>
+            <if test="asrProvider != null and asrProvider != ''">asr_provider,</if>
+            <if test="aiTransferType != null and aiTransferType != ''">ai_transfer_type,</if>
+            <if test="aiTransferData != null and aiTransferData != ''">ai_transfer_data,</if>
+            <if test="autoStop != null ">auto_stop,</if>
+            <if test="ivrId != null and ivrId != ''">ivr_id,</if>
+        </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="groupId != null and groupId != ''">#{groupId},</if>
+            <if test="batchName != null and batchName != ''">#{batchName},</if>
+            <if test="ifcall != null">#{ifcall},</if>
+            <if test="rate != null">#{rate},</if>
+            <if test="threadNum != null">#{threadNum},</if>
+            <if test="createtime != null">#{createtime},</if>
+            <if test="executing != null">#{executing},</if>
+            <if test="stopTime != null">#{stopTime},</if>
+            <if test="userid != null and userid != ''">#{userid},</if>
+            <if test="taskType != null">#{taskType},</if>
+            <if test="gatewayId != null">#{gatewayId},</if>
+            <if test="voiceCode != null and voiceCode != ''">#{voiceCode},</if>
+            <if test="voiceSource != null and voiceSource != ''">#{voiceSource},</if>
+            <if test="avgRingTimeLen != null">#{avgRingTimeLen},</if>
+            <if test="avgCallTalkTimeLen != null">#{avgCallTalkTimeLen},</if>
+            <if test="avgCallEndProcessTimeLen != null">#{avgCallEndProcessTimeLen},</if>
+            <if test="callNodeNo != null">#{callNodeNo},</if>
+            <if test="llmAccountId != null">#{llmAccountId},</if>
+            <if test="playTimes != null">#{playTimes},</if>
+            <if test="asrProvider != null and asrProvider != ''">#{asrProvider},</if>
+            <if test="aiTransferType != null and aiTransferType != ''">#{aiTransferType},</if>
+            <if test="aiTransferData != null and aiTransferData != ''">#{aiTransferData},</if>
+            <if test="autoStop != null ">#{autoStop},</if>
+            <if test="ivrId != null and ivrId != ''">#{ivrId},</if>
+        </trim>
+    </insert>
+
+    <update id="updateCcCallTask" parameterType="CcCallTask">
+        update cc_call_task
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="groupId != null and groupId != ''">group_id = #{groupId},</if>
+            <if test="batchName != null and batchName != ''">batch_name = #{batchName},</if>
+            <if test="ifcall != null">ifcall = #{ifcall},</if>
+            <if test="rate != null">rate = #{rate},</if>
+            <if test="threadNum != null">thread_num = #{threadNum},</if>
+            <if test="createtime != null">createtime = #{createtime},</if>
+            <if test="executing != null">executing = #{executing},</if>
+            <if test="stopTime != null">stop_time = #{stopTime},</if>
+            <if test="userid != null and userid != ''">userid = #{userid},</if>
+            <if test="gatewayId != null">gateway_id = #{gatewayId},</if>
+            <if test="taskType != null">task_type = #{taskType},</if>
+            <if test="voiceCode != null and voiceCode != ''">voice_code = #{voiceCode},</if>
+            <if test="voiceSource != null and voiceSource != ''">voice_source = #{voiceSource},</if>
+            <if test="avgRingTimeLen != null">avg_ring_time_len = #{avgRingTimeLen},</if>
+            <if test="avgCallTalkTimeLen != null">avg_call_talk_time_len = #{avgCallTalkTimeLen},</if>
+            <if test="avgCallEndProcessTimeLen != null">avg_call_end_process_time_len = #{avgCallEndProcessTimeLen},</if>
+            <if test="callNodeNo != null">call_node_no = #{callNodeNo},</if>
+            <if test="llmAccountId != null">llm_account_id = #{llmAccountId},</if>
+            <if test="playTimes != null">play_times = #{playTimes},</if>
+            <if test="asrProvider != null and asrProvider != ''">asr_provider = #{asrProvider},</if>
+            <if test="aiTransferType != null and aiTransferType != ''">ai_transfer_type = #{aiTransferType},</if>
+            <if test="aiTransferData != null and aiTransferData != ''">ai_transfer_data = #{aiTransferData},</if>
+            <if test="autoStop != null ">auto_stop = #{autoStop},</if>
+            <if test="ivrId != null and ivrId != ''">ivr_id = #{ivrId},</if>
+
+        </trim>
+        where batch_id = #{batchId}
+    </update>
+
+    <delete id="deleteCcCallTaskByBatchId" parameterType="Long">
+        delete from cc_call_task where batch_id = #{batchId}
+    </delete>
+
+    <delete id="deleteCcCallTaskByBatchIds" parameterType="String">
+        delete from cc_call_task where batch_id in 
+        <foreach item="batchId" collection="array" open="(" separator="," close=")">
+            #{batchId}
+        </foreach>
+    </delete>
+
+
+    <select id="selectCcCallTaskByBatchName" resultMap="CcCallTaskResult">
+        <include refid="selectCcCallTaskVo"/>
+        where batch_name = #{batchName} and task_type = #{taskType}
+    </select>
+
+    <insert id="bakCallTaskByBatchId" parameterType="Long">
+        insert into his_cc_call_task select * from cc_call_task where batch_id = #{batchId}
+    </insert>
+
+</mapper>

+ 106 - 0
fs-service/src/main/resources/mapper/aicall/CcLlmAgentAccountMapper.xml

@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.aicall.mapper.CcLlmAgentAccountMapper">
+    
+    <resultMap type="CcLlmAgentAccount" id="CcLlmAgentAccountResult">
+        <result property="id"    column="id"    />
+        <result property="name"    column="name"    />
+        <result property="accountJson"    column="account_json"    />
+        <result property="providerClassName"    column="provider_class_name"    />
+        <result property="accountEntity"    column="account_entity"    />
+        <result property="interruptFlag"    column="interrupt_flag"    />
+        <result property="interruptKeywords"    column="interrupt_keywords"    />
+        <result property="interruptIgnoreKeywords"    column="interrupt_ignore_keywords"    />
+        <result property="intentionTips"    column="intention_tips"    />
+        <result property="concurrentNum"    column="concurrent_num"    />
+        <result property="transferManualDigit"    column="transfer_manual_digit"    />
+        <result property="kbCatId"    column="kb_cat_id"    />
+    </resultMap>
+
+    <sql id="selectCcLlmAgentAccountVo">
+        select id, name, account_json, provider_class_name, account_entity, interrupt_flag, interrupt_keywords, interrupt_ignore_keywords, intention_tips, concurrent_num, transfer_manual_digit, kb_cat_id from cc_llm_agent_account
+    </sql>
+
+    <select id="selectCcLlmAgentAccountList" parameterType="CcLlmAgentAccount" resultMap="CcLlmAgentAccountResult">
+        <include refid="selectCcLlmAgentAccountVo"/>
+        <where>  
+            <if test="name != null  and name != ''"> and name like concat('%', #{name}, '%')</if>
+            <if test="providerClassName != null  and providerClassName != ''"> and provider_class_name like concat('%', #{providerClassName}, '%')</if>
+            <if test="modelIds != null and modelIds.size() > 0"> and id in
+                <foreach item="modelId" collection="modelIds" open="(" separator="," close=")">
+                    #{modelId}
+                </foreach>
+            </if>
+        </where>
+        order by id desc
+    </select>
+    
+    <select id="selectCcLlmAgentAccountById" parameterType="Integer" resultMap="CcLlmAgentAccountResult">
+        <include refid="selectCcLlmAgentAccountVo"/>
+        where id = #{id}
+    </select>
+
+    <insert id="insertCcLlmAgentAccount" parameterType="CcLlmAgentAccount" useGeneratedKeys="true"  keyProperty="id">
+        insert into cc_llm_agent_account
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            id,
+            name,
+            account_json,
+            provider_class_name,
+            account_entity,
+            interrupt_flag,
+            interrupt_keywords,
+            interrupt_ignore_keywords,
+            intention_tips,
+            concurrent_num,
+            transfer_manual_digit,
+            kb_cat_id,
+        </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+                   #{id},
+                   #{name},
+                   #{accountJson},
+                   #{providerClassName},
+                   #{accountEntity},
+                   #{interruptFlag},
+                   #{interruptKeywords},
+                   #{interruptIgnoreKeywords},
+                   #{intentionTips},
+                   #{concurrentNum},
+                   #{transferManualDigit},
+                   #{kbCatId},
+        </trim>
+    </insert>
+
+    <update id="updateCcLlmAgentAccount" parameterType="CcLlmAgentAccount">
+        update cc_llm_agent_account
+        <trim prefix="SET" suffixOverrides=",">
+            name = #{name},
+            account_json = #{accountJson},
+            provider_class_name = #{providerClassName},
+            account_entity = #{accountEntity},
+            interrupt_flag = #{interruptFlag},
+            interrupt_keywords = #{interruptKeywords},
+            interrupt_ignore_keywords = #{interruptIgnoreKeywords},
+            intention_tips = #{intentionTips},
+            concurrent_num = #{concurrentNum},
+            transfer_manual_digit = #{transferManualDigit},
+            kb_cat_id = #{kbCatId},
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteCcLlmAgentAccountById" parameterType="Integer">
+        delete from cc_llm_agent_account where id = #{id}
+    </delete>
+
+    <delete id="deleteCcLlmAgentAccountByIds" parameterType="String">
+        delete from cc_llm_agent_account where id in 
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+
+</mapper>

+ 62 - 0
fs-service/src/main/resources/mapper/aicall/CcLlmAgentProviderMapper.xml

@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.aicall.mapper.CcLlmAgentProviderMapper">
+    
+    <resultMap type="CcLlmAgentProvider" id="CcLlmAgentProviderResult">
+        <result property="id"    column="id"    />
+        <result property="providerClassName"    column="provider_class_name"    />
+        <result property="note"    column="note"    />
+    </resultMap>
+
+    <sql id="selectCcLlmAgentProviderVo">
+        select id, provider_class_name, note from cc_llm_agent_provider
+    </sql>
+
+    <select id="selectCcLlmAgentProviderList" parameterType="CcLlmAgentProvider" resultMap="CcLlmAgentProviderResult">
+        <include refid="selectCcLlmAgentProviderVo"/>
+        <where>  
+            <if test="providerClassName != null  and providerClassName != ''"> and provider_class_name like concat('%', #{providerClassName}, '%')</if>
+            <if test="note != null  and note != ''"> and note = #{note}</if>
+        </where>
+    </select>
+    
+    <select id="selectCcLlmAgentProviderById" parameterType="Integer" resultMap="CcLlmAgentProviderResult">
+        <include refid="selectCcLlmAgentProviderVo"/>
+        where id = #{id}
+    </select>
+
+    <insert id="insertCcLlmAgentProvider" parameterType="CcLlmAgentProvider" useGeneratedKeys="true" keyProperty="id">
+        insert into cc_llm_agent_provider
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            provider_class_name,
+            note,
+        </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            #{providerClassName},
+            #{note},
+        </trim>
+    </insert>
+
+    <update id="updateCcLlmAgentProvider" parameterType="CcLlmAgentProvider">
+        update cc_llm_agent_provider
+        <trim prefix="SET" suffixOverrides=",">
+            provider_class_name = #{providerClassName},
+            note = #{note},
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteCcLlmAgentProviderById" parameterType="Integer">
+        delete from cc_llm_agent_provider where id = #{id}
+    </delete>
+
+    <delete id="deleteCcLlmAgentProviderByIds" parameterType="String">
+        delete from cc_llm_agent_provider where id in 
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+
+</mapper>

+ 62 - 0
fs-service/src/main/resources/mapper/aicall/CcLlmKbCatMapper.xml

@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.aicall.mapper.CcLlmKbCatMapper">
+    
+    <resultMap type="CcLlmKbCat" id="CcLlmKbCatResult">
+        <result property="id"    column="id"    />
+        <result property="cat"    column="cat"    />
+        <result property="description"    column="description"    />
+    </resultMap>
+
+    <sql id="selectCcLlmKbCatVo">
+        select id, cat, description from cc_llm_kb_cat
+    </sql>
+
+    <select id="selectCcLlmKbCatList" parameterType="CcLlmKbCat" resultMap="CcLlmKbCatResult">
+        <include refid="selectCcLlmKbCatVo"/>
+        <where>  
+            <if test="cat != null  and cat != ''"> and cat = #{cat}</if>
+            <if test="description != null  and description != ''"> and description = #{description}</if>
+        </where>
+    </select>
+    
+    <select id="selectCcLlmKbCatById" parameterType="Long" resultMap="CcLlmKbCatResult">
+        <include refid="selectCcLlmKbCatVo"/>
+        where id = #{id}
+    </select>
+
+    <insert id="insertCcLlmKbCat" parameterType="CcLlmKbCat" useGeneratedKeys="true" keyProperty="id">
+        insert into cc_llm_kb_cat
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="cat != null">cat,</if>
+            <if test="description != null">description,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="cat != null">#{cat},</if>
+            <if test="description != null">#{description},</if>
+         </trim>
+    </insert>
+
+    <update id="updateCcLlmKbCat" parameterType="CcLlmKbCat">
+        update cc_llm_kb_cat
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="cat != null">cat = #{cat},</if>
+            <if test="description != null">description = #{description},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteCcLlmKbCatById" parameterType="Long">
+        delete from cc_llm_kb_cat where id = #{id}
+    </delete>
+
+    <delete id="deleteCcLlmKbCatByIds" parameterType="String">
+        delete from cc_llm_kb_cat where id in 
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+
+</mapper>

+ 90 - 0
fs-service/src/main/resources/mapper/aicall/CcLlmKbMapper.xml

@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.aicall.mapper.CcLlmKbMapper">
+    
+    <resultMap type="CcLlmKb" id="CcLlmKbResult">
+        <result property="id"    column="id"    />
+        <result property="title"    column="title"    />
+        <result property="content"    column="content"    />
+        <result property="catId"    column="cat_id"    />
+    </resultMap>
+
+    <sql id="selectCcLlmKbVo">
+        select id, title, content, cat_id from cc_llm_kb
+    </sql>
+
+    <select id="selectCcLlmKbList" parameterType="CcLlmKb" resultMap="CcLlmKbResult">
+        <include refid="selectCcLlmKbVo"/>
+        <where>  
+            <if test="title != null  and title != ''"> and title = #{title}</if>
+            <if test="content != null  and content != ''"> and content = #{content}</if>
+            <if test="catId != null "> and cat_id = #{catId}</if>
+        </where>
+    </select>
+    
+    <select id="selectCcLlmKbById" parameterType="Long" resultMap="CcLlmKbResult">
+        <include refid="selectCcLlmKbVo"/>
+        where id = #{id}
+    </select>
+
+    <insert id="insertCcLlmKb" parameterType="CcLlmKb">
+        insert into cc_llm_kb
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="id != null">id,</if>
+            <if test="title != null and title != ''">title,</if>
+            <if test="content != null and content != ''">content,</if>
+            <if test="catId != null">cat_id,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="id != null">#{id},</if>
+            <if test="title != null and title != ''">#{title},</if>
+            <if test="content != null and content != ''">#{content},</if>
+            <if test="catId != null">#{catId},</if>
+         </trim>
+    </insert>
+
+    <update id="updateCcLlmKb" parameterType="CcLlmKb">
+        update cc_llm_kb
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="title != null and title != ''">title = #{title},</if>
+            <if test="content != null and content != ''">content = #{content},</if>
+            <if test="catId != null">cat_id = #{catId},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteCcLlmKbById" parameterType="Long">
+        delete from cc_llm_kb where id = #{id}
+    </delete>
+
+    <delete id="deleteCcLlmKbByIds" parameterType="String">
+        delete from cc_llm_kb where id in 
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+
+    <select id="selectCountByCatId" parameterType="Long">
+        select count(1) from cc_llm_kb where cat_id = #{catId}
+    </select>
+
+
+    <delete id="deleteCcLlmKbByCatId" parameterType="Long">
+        delete from cc_llm_kb where cat_id = #{catId}
+    </delete>
+
+    <!-- 批量插入 -->
+    <insert id="insertBatch" parameterType="java.util.List">
+        INSERT INTO cc_llm_kb
+        (title, content, cat_id)
+        VALUES
+        <foreach collection="list" item="e" separator=",">
+            (#{e.title},
+            #{e.content},
+            #{e.catId})
+        </foreach>
+    </insert>
+
+</mapper>

+ 82 - 0
fs-service/src/main/resources/mapper/aicall/CcParamsMapper.xml

@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.aicall.mapper.CcParamsMapper">
+    
+    <resultMap type="CcParams" id="CcParamsResult">
+        <result property="id"    column="id"    />
+        <result property="paramName"    column="param_name"    />
+        <result property="paramCode"    column="param_code"    />
+        <result property="paramValue"    column="param_value"    />
+        <result property="hideValue"    column="hide_value"    />
+        <result property="paramType"    column="param_type"    />
+        <result property="allowEdit"    column="allow_edit"    />
+    </resultMap>
+
+    <sql id="selectCcParamsVo">
+        select id, param_name, param_code, param_value, param_type,hide_value, allow_edit from cc_params
+    </sql>
+
+    <select id="selectCcParamsList" parameterType="CcParams" resultMap="CcParamsResult">
+        <include refid="selectCcParamsVo"/>
+        <where>  
+            <if test="paramName != null  and paramName != ''"> and param_name like concat('%', #{paramName}, '%')</if>
+            <if test="paramCode != null  and paramCode != ''"> and param_code = #{paramCode}</if>
+            <if test="paramValue != null  and paramValue != ''"> and param_value = #{paramValue}</if>
+            <if test="paramType != null  and paramType != ''"> and param_type = #{paramType}</if>
+        </where>
+    </select>
+    
+    <select id="selectCcParamsById" parameterType="Long" resultMap="CcParamsResult">
+        <include refid="selectCcParamsVo"/>
+        where id = #{id}
+    </select>
+
+    <insert id="insertCcParams" parameterType="CcParams" useGeneratedKeys="true" keyProperty="id">
+        insert into cc_params
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="paramName != null and paramName != ''">param_name,</if>
+            <if test="paramCode != null and paramCode != ''">param_code,</if>
+            <if test="paramValue != null and paramValue != ''">param_value,</if>
+            <if test="paramType != null and paramType != ''">param_type,</if>
+            <if test="allowEdit != null and allowEdit != ''">allow_edit,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="paramName != null and paramName != ''">#{paramName},</if>
+            <if test="paramCode != null and paramCode != ''">#{paramCode},</if>
+            <if test="paramValue != null and paramValue != ''">#{paramValue},</if>
+            <if test="paramType != null and paramType != ''">#{paramType},</if>
+            <if test="allowEdit != null and allowEdit != ''">#{allowEdit},</if>
+         </trim>
+    </insert>
+
+    <update id="updateCcParams" parameterType="CcParams">
+        update cc_params
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="paramName != null and paramName != ''">param_name = #{paramName},</if>
+            <if test="paramCode != null and paramCode != ''">param_code = #{paramCode},</if>
+            <if test="paramValue != null and paramValue != ''">param_value = #{paramValue},</if>
+            <if test="paramType != null and paramType != ''">param_type = #{paramType},</if>
+            <if test="allowEdit != null and allowEdit != ''">allow_edit = #{allowEdit},</if>
+
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteCcParamsById" parameterType="Long">
+        delete from cc_params where id = #{id}
+    </delete>
+
+    <delete id="deleteCcParamsByIds" parameterType="String">
+        delete from cc_params where id in 
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+
+    <update id="updateParamsValue" parameterType="String">
+        update cc_params set param_value = #{paramValue} where param_code = #{paramCode}
+    </update>
+
+</mapper>

+ 80 - 0
fs-service/src/main/resources/mapper/aicall/CompanyBindAiModelMapper.xml

@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.aicall.mapper.CompanyBindAiModelMapper">
+    <resultMap type="com.fs.aicall.domain.CompanyBindAiModel" id="CompanyBindAiModelResult">
+        <id property="id" column="id" />
+        <result property="companyId" column="company_id" />
+        <result property="modelId" column="model_id" />
+        <result property="createTime" column="create_time" />
+        <result property="updateTime" column="update_time" />
+    </resultMap>
+
+    <sql id="selectCompanyBindAiModelVo">
+        select id, company_id, model_id, create_time, update_time from company_bind_ai_model
+    </sql>
+
+    <select id="selectCompanyBindAiModelById" parameterType="Long" resultMap="CompanyBindAiModelResult">
+        <include refid="selectCompanyBindAiModelVo" />
+        where id = #{id}
+    </select>
+
+    <select id="selectCompanyBindAiModelList" parameterType="com.fs.aicall.domain.CompanyBindAiModel" resultMap="CompanyBindAiModelResult">
+        <include refid="selectCompanyBindAiModelVo" />
+        <where>
+            <if test="companyId != null">
+                and company_id = #{companyId}
+            </if>
+            <if test="modelId != null">
+                and model_id = #{modelId}
+            </if>
+        </where>
+    </select>
+
+    <insert id="insertCompanyBindAiModel" parameterType="com.fs.aicall.domain.CompanyBindAiModel" useGeneratedKeys="true" keyProperty="id">
+        insert into company_bind_ai_model(company_id, model_id, create_time, update_time)
+        values (#{companyId}, #{modelId}, #{createTime}, #{updateTime})
+    </insert>
+
+    <update id="updateCompanyBindAiModel" parameterType="com.fs.aicall.domain.CompanyBindAiModel">
+        update company_bind_ai_model
+        <set>
+            <if test="companyId != null">
+                company_id = #{companyId},
+            </if>
+            <if test="modelId != null">
+                model_id = #{modelId},
+            </if>
+            update_time = now()
+        </set>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteCompanyBindAiModelById" parameterType="Long">
+        delete from company_bind_ai_model where id = #{id}
+    </delete>
+
+    <delete id="deleteCompanyBindAiModelByIds" parameterType="Long[]">
+        delete from company_bind_ai_model where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+
+    <select id="selectModelIdsByCompanyId" parameterType="Long" resultType="Long">
+        select model_id from company_bind_ai_model where company_id = #{companyId}
+    </select>
+
+    <delete id="deleteCompanyBindAiModelByModelId" parameterType="Long">
+        delete from company_bind_ai_model where model_id = #{modelId}
+    </delete>
+
+    <insert id="batchInsertCompanyBindAiModel">
+        insert into company_bind_ai_model(company_id, model_id, create_time, update_time)
+        values
+        <foreach item="modelId" collection="modelIds" separator=",">
+            (#{companyId}, #{modelId}, now(), now())
+        </foreach>
+    </insert>
+</mapper>