diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/controller/CallCenterController.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/controller/CallCenterController.java index cffc2f40e4..b389763512 100644 --- a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/controller/CallCenterController.java +++ b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/controller/CallCenterController.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2025-06-03 14:30:25 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2025-06-03 09:28:50 + * @LastEditTime: 2025-06-03 10:07:00 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -26,9 +26,6 @@ import com.bytedesk.freeswitch.service.CallStatisticsService; @Controller @RequestMapping("/callcenter") public class CallCenterController { - - // @Autowired - // private CallService callService; @Autowired private CallStatisticsService callStatisticsService; diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/controller/CallController.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/controller/CallController.java index 294d053c6c..5a54cd422e 100644 --- a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/controller/CallController.java +++ b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/controller/CallController.java @@ -1,5 +1,6 @@ package com.bytedesk.freeswitch.controller; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -9,23 +10,21 @@ import org.springframework.web.bind.annotation.RestController; import com.bytedesk.freeswitch.service.CallService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import java.util.Map; /** * 呼叫控制器 */ +@Slf4j @RestController @RequestMapping("/api/v1/call") @RequiredArgsConstructor +@ConditionalOnProperty(name = "bytedesk.freeswitch.enabled", havingValue = "true", matchIfMissing = false) public class CallController { - private static final Logger log = LoggerFactory.getLogger(CallController.class); - private final CallService callService; /** diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/handler/FreeSwitchEventListener.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/handler/FreeSwitchEventListener.java index d5f3df36c1..d1570523d2 100644 --- a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/handler/FreeSwitchEventListener.java +++ b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/handler/FreeSwitchEventListener.java @@ -4,7 +4,7 @@ import org.freeswitch.esl.client.IEslEventListener; import org.freeswitch.esl.client.transport.event.EslEvent; import org.springframework.stereotype.Component; -import com.bytedesk.freeswitch.service.CallService; +import com.bytedesk.freeswitch.service.ICallService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -17,7 +17,7 @@ import lombok.extern.slf4j.Slf4j; @RequiredArgsConstructor public class FreeSwitchEventListener implements IEslEventListener { - private final CallService callService; + private final ICallService callService; /** * 处理FreeSwitch事件 diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/service/CallService.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/service/CallService.java index 5c65cfd87e..ca328a2b4b 100644 --- a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/service/CallService.java +++ b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/service/CallService.java @@ -9,7 +9,6 @@ import org.freeswitch.esl.client.transport.SendMsg; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Service; -import com.bytedesk.freeswitch.config.FreeSwitchProperties; import com.bytedesk.freeswitch.model.CallInfo; import lombok.RequiredArgsConstructor; @@ -22,7 +21,7 @@ import lombok.extern.slf4j.Slf4j; @Service @RequiredArgsConstructor @ConditionalOnProperty(name = "bytedesk.freeswitch.enabled", havingValue = "true", matchIfMissing = false) -public class CallService { +public class CallService implements ICallService { private final Client eslClient; // private final FreeSwitchProperties freeSwitchProperties; diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/service/CallStatisticsService.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/service/CallStatisticsService.java index 067fa5db79..4995104e9c 100644 --- a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/service/CallStatisticsService.java +++ b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/service/CallStatisticsService.java @@ -13,19 +13,42 @@ */ package com.bytedesk.freeswitch.service; +import org.springframework.stereotype.Service; + import com.bytedesk.freeswitch.model.CallStatistics; +import lombok.extern.slf4j.Slf4j; + /** * 呼叫统计服务接口 */ -public interface CallStatisticsService { +@Slf4j +@Service +public class CallStatisticsService { /** * 获取当前呼叫统计数据 * * @return 呼叫统计数据 */ - CallStatistics getCallStatistics(); + public CallStatistics getCallStatistics() { + // 这里应当从数据库中获取真实数据 + // 当前为示例数据 + log.info("获取当前呼叫统计数据"); + + CallStatistics stats = new CallStatistics(); + stats.setTodayCallCount(125); + stats.setAverageCallDuration(3.7); + stats.setOnlineAgentsCount(12); + stats.setActiveCallsCount(8); + stats.setInboundCallsCount(85); + stats.setOutboundCallsCount(40); + stats.setAnsweredCallsCount(110); + stats.setMissedCallsCount(15); + stats.setAnswerRate(88.0); + + return stats; + } /** * 根据时间周期获取呼叫统计数据 @@ -33,5 +56,49 @@ public interface CallStatisticsService { * @param period 时间周期(today, week, month, year) * @return 呼叫统计数据 */ - CallStatistics getCallStatisticsByPeriod(String period); + public CallStatistics getCallStatisticsByPeriod(String period) { + log.info("获取{}周期的呼叫统计数据", period); + + CallStatistics stats = new CallStatistics(); + + // 根据不同时间周期返回相应的统计数据 + if ("today".equals(period)) { + return getCallStatistics(); + } else if ("week".equals(period)) { + stats.setTodayCallCount(820); + stats.setAverageCallDuration(4.2); + stats.setOnlineAgentsCount(14); + stats.setActiveCallsCount(8); + stats.setInboundCallsCount(550); + stats.setOutboundCallsCount(270); + stats.setAnsweredCallsCount(720); + stats.setMissedCallsCount(100); + stats.setAnswerRate(87.8); + } else if ("month".equals(period)) { + stats.setTodayCallCount(3240); + stats.setAverageCallDuration(3.9); + stats.setOnlineAgentsCount(15); + stats.setActiveCallsCount(8); + stats.setInboundCallsCount(2180); + stats.setOutboundCallsCount(1060); + stats.setAnsweredCallsCount(2950); + stats.setMissedCallsCount(290); + stats.setAnswerRate(91.0); + } else if ("year".equals(period)) { + stats.setTodayCallCount(36500); + stats.setAverageCallDuration(4.0); + stats.setOnlineAgentsCount(15); + stats.setActiveCallsCount(8); + stats.setInboundCallsCount(24500); + stats.setOutboundCallsCount(12000); + stats.setAnsweredCallsCount(33200); + stats.setMissedCallsCount(3300); + stats.setAnswerRate(90.7); + } else { + // 默认返回当天数据 + return getCallStatistics(); + } + + return stats; + } } diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/service/DefaultCallService.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/service/DefaultCallService.java new file mode 100644 index 0000000000..9711dce574 --- /dev/null +++ b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/service/DefaultCallService.java @@ -0,0 +1,85 @@ +/* + * @Author: GitHub Copilot + * @Date: 2025-06-03 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * + * Copyright (c) 2025 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.freeswitch.service; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Service; + +import lombok.extern.slf4j.Slf4j; + +/** + * 默认呼叫服务实现,当FreeSwitch未启用时使用 + */ +@Slf4j +@Service +@ConditionalOnProperty(name = "bytedesk.freeswitch.enabled", havingValue = "false", matchIfMissing = true) +public class DefaultCallService implements ICallService { + + public DefaultCallService() { + log.info("创建默认呼叫服务(FreeSwitch未启用)"); + } + + @Override + public String makeCall(String fromUser, String toUser) { + log.warn("FreeSwitch未启用,无法发起呼叫"); + return null; + } + + @Override + public boolean answerCall(String callId) { + log.warn("FreeSwitch未启用,无法应答呼叫"); + return false; + } + + @Override + public boolean rejectCall(String callId) { + log.warn("FreeSwitch未启用,无法拒绝呼叫"); + return false; + } + + @Override + public boolean endCall(String callId) { + log.warn("FreeSwitch未启用,无法结束呼叫"); + return false; + } + + @Override + public boolean sendDtmf(String callId, String digit) { + log.warn("FreeSwitch未启用,无法发送DTMF"); + return false; + } + + @Override + public boolean toggleMute(String callId, boolean mute) { + log.warn("FreeSwitch未启用,无法{}静音", mute ? "开启" : "关闭"); + return false; + } + + @Override + public void handleCallStart(String callerId, String destination, String uuid) { + // 无操作 + } + + @Override + public void handleCallAnswered(String uuid) { + // 无操作 + } + + @Override + public void handleCallEnd(String uuid, String hangupCause) { + // 无操作 + } + + @Override + public void handleDtmf(String uuid, String digit) { + // 无操作 + } +} diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/service/ICallService.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/service/ICallService.java new file mode 100644 index 0000000000..3e8ce2a8f5 --- /dev/null +++ b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/service/ICallService.java @@ -0,0 +1,100 @@ +/* + * @Author: GitHub Copilot + * @Date: 2025-06-03 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * + * Copyright (c) 2025 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.freeswitch.service; + +/** + * 呼叫服务接口 + */ +public interface ICallService { + + /** + * 发起呼叫 + * + * @param fromUser 主叫用户ID + * @param toUser 被叫用户ID + * @return 呼叫ID + */ + String makeCall(String fromUser, String toUser); + + /** + * 应答呼叫 + * + * @param callId 呼叫ID + * @return 是否成功 + */ + boolean answerCall(String callId); + + /** + * 拒绝呼叫 + * + * @param callId 呼叫ID + * @return 是否成功 + */ + boolean rejectCall(String callId); + + /** + * 结束呼叫 + * + * @param callId 呼叫ID + * @return 是否成功 + */ + boolean endCall(String callId); + + /** + * 发送DTMF按键 + * + * @param callId 呼叫ID + * @param digit 按键值 + * @return 是否成功 + */ + boolean sendDtmf(String callId, String digit); + + /** + * 静音/取消静音 + * + * @param callId 呼叫ID + * @param mute 是否静音 + * @return 是否成功 + */ + boolean toggleMute(String callId, boolean mute); + + /** + * 处理呼叫开始 + * + * @param callerId 主叫号码 + * @param destination 被叫号码 + * @param uuid 呼叫UUID + */ + void handleCallStart(String callerId, String destination, String uuid); + + /** + * 处理呼叫应答 + * + * @param uuid 呼叫UUID + */ + void handleCallAnswered(String uuid); + + /** + * 处理呼叫结束 + * + * @param uuid 呼叫UUID + * @param hangupCause 挂断原因 + */ + void handleCallEnd(String uuid, String hangupCause); + + /** + * 处理DTMF按键 + * + * @param uuid 呼叫UUID + * @param digit 按键值 + */ + void handleDtmf(String uuid, String digit); +} diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/service/impl/CallStatisticsServiceImpl.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/service/impl/CallStatisticsServiceImpl.java deleted file mode 100644 index 3447ee5e8b..0000000000 --- a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/service/impl/CallStatisticsServiceImpl.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * @Author: jackning 270580156@qq.com - * @Date: 2025-06-03 15:18:15 - * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2025-06-03 09:29:18 - * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * contact: 270580156@qq.com - * 联系:270580156@qq.com - * Copyright (c) 2025 by bytedesk.com, All Rights Reserved. - */ -package com.bytedesk.freeswitch.service.impl; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Service; - -import com.bytedesk.freeswitch.model.CallStatistics; -import com.bytedesk.freeswitch.service.CallStatisticsService; - -/** - * 呼叫统计服务实现类 - */ -@Service -public class CallStatisticsServiceImpl implements CallStatisticsService { - - private static final Logger log = LoggerFactory.getLogger(CallStatisticsServiceImpl.class); - - @Override - public CallStatistics getCallStatistics() { - // 这里应当从数据库中获取真实数据 - // 当前为示例数据 - log.info("获取当前呼叫统计数据"); - - CallStatistics stats = new CallStatistics(); - stats.setTodayCallCount(125); - stats.setAverageCallDuration(3.7); - stats.setOnlineAgentsCount(12); - stats.setActiveCallsCount(8); - stats.setInboundCallsCount(85); - stats.setOutboundCallsCount(40); - stats.setAnsweredCallsCount(110); - stats.setMissedCallsCount(15); - stats.setAnswerRate(88.0); - - return stats; - } - - @Override - public CallStatistics getCallStatisticsByPeriod(String period) { - log.info("获取{}周期的呼叫统计数据", period); - - CallStatistics stats = new CallStatistics(); - - // 根据不同时间周期返回相应的统计数据 - if ("today".equals(period)) { - return getCallStatistics(); - } else if ("week".equals(period)) { - stats.setTodayCallCount(820); - stats.setAverageCallDuration(4.2); - stats.setOnlineAgentsCount(14); - stats.setActiveCallsCount(8); - stats.setInboundCallsCount(550); - stats.setOutboundCallsCount(270); - stats.setAnsweredCallsCount(720); - stats.setMissedCallsCount(100); - stats.setAnswerRate(87.8); - } else if ("month".equals(period)) { - stats.setTodayCallCount(3240); - stats.setAverageCallDuration(3.9); - stats.setOnlineAgentsCount(15); - stats.setActiveCallsCount(8); - stats.setInboundCallsCount(2180); - stats.setOutboundCallsCount(1060); - stats.setAnsweredCallsCount(2950); - stats.setMissedCallsCount(290); - stats.setAnswerRate(91.0); - } else if ("year".equals(period)) { - stats.setTodayCallCount(36500); - stats.setAverageCallDuration(4.0); - stats.setOnlineAgentsCount(15); - stats.setActiveCallsCount(8); - stats.setInboundCallsCount(24500); - stats.setOutboundCallsCount(12000); - stats.setAnsweredCallsCount(33200); - stats.setMissedCallsCount(3300); - stats.setAnswerRate(90.7); - } else { - // 默认返回当天数据 - return getCallStatistics(); - } - - return stats; - } -} diff --git a/plugins/freeswitch/src/main/resources/application.properties b/plugins/freeswitch/src/main/resources/application.properties index 081729180c..515e123aa4 100644 --- a/plugins/freeswitch/src/main/resources/application.properties +++ b/plugins/freeswitch/src/main/resources/application.properties @@ -1 +1,13 @@ -spring.application.name=baidu +spring.application.name=freeswitch + +# FreeSwitch配置 +bytedesk.freeswitch.enabled=false +bytedesk.freeswitch.server=127.0.0.1 +bytedesk.freeswitch.eslPort=8021 +bytedesk.freeswitch.eslPassword=bytedesk123 +bytedesk.freeswitch.sipPort=15060 +bytedesk.freeswitch.webrtcPort=17443 +bytedesk.freeswitch.wsPort=15066 +bytedesk.freeswitch.callTimeout=60 +bytedesk.freeswitch.rtpPortStart=16000 +bytedesk.freeswitch.rtpPortEnd=16129 diff --git a/plugins/freeswitch/src/main/resources/templates/ftl/callcenter/index.ftl b/plugins/freeswitch/src/main/resources/templates/ftl/callcenter/index.ftl index 0225e83d84..bbdfc5e4b2 100644 --- a/plugins/freeswitch/src/main/resources/templates/ftl/callcenter/index.ftl +++ b/plugins/freeswitch/src/main/resources/templates/ftl/callcenter/index.ftl @@ -1,223 +1,232 @@ <#import "layout/base.ftl" as base> -<@base.base title="ByteDesk 呼叫中心 - 企业级通信解决方案"> +<@base.base title="ByteDesk 智能呼叫中心 - AI驱动的企业级通信解决方案">
${statistics.todayCallCount!'0'}
-${statistics.averageCallDuration!'0'}分钟
-${statistics.onlineAgentsCount!'0'}
-${statistics.activeCallsCount!'0'}
-FreeSWITCH是一个开源通信平台,支持VoIP、WebRTC和传统电话网络,提供强大的通信能力。
-结合大语言模型,打造智能化客户服务体验
根据客户需求和座席技能自动分配呼叫,提高服务质量和效率。
+ +基于大语言模型的智能语音助手,7x24小时不间断服务,自动处理80%的常见咨询,显著降低人工成本。
通过详细的呼叫统计和报表,帮助企业优化客户服务流程。
+AI驱动的数据分析系统,自动识别客户意图,生成服务洞察,助力企业优化服务流程。
基于AI的智能路由系统,根据客户画像和座席技能自动匹配,提升服务效率和客户满意度。
+全面的功能支持,满足企业多样化需求
支持SIP、WebRTC、PSTN等多种通信协议,实现全渠道客户沟通。
+支持SIP、WebRTC、PSTN等多种通信协议,实现全渠道客户沟通,确保服务无死角。
+自动录制所有通话,支持质量控制和培训需求。
+AI驱动的智能质检系统,自动分析通话内容,提供质量评估和改进建议。
+通过可视化界面轻松构建交互式语音应答系统。
+基于AI的智能语音导航系统,提供自然流畅的语音交互体验。
+与客户关系管理系统无缝集成,提供完整的客户服务视图。
+AI驱动的数据分析平台,提供深度业务洞察,助力决策优化。
+
+ 为企业创造实实在在的价值
+AI自动化处理80%常见咨询,显著降低人工成本
+智能路由和AI助手提升服务效率达300%
+智能营销和交叉销售提升转化率30%
+个性化服务提升客户满意度至95%
ByteDesk呼叫中心基于FreeSWITCH构建,采用微服务架构,具有高可用性和可扩展性。
企业级技术架构,确保系统稳定可靠
企业级安全架构,数据加密传输,多重备份机制,确保系统安全可靠。
+支持公有云、私有云、混合云多种部署方式,满足不同企业需求。
+分布式架构设计,支持水平扩展,确保系统7x24小时稳定运行。
+值得信赖的企业级解决方案
+通过部署ByteDesk智能呼叫中心,客服效率提升300%,客户满意度提升40%。
AI驱动的智能服务系统,帮助客户服务成本降低50%,业务转化率提升35%。
+智能呼叫中心助力招生咨询效率提升200%,客户转化率提升45%。