diff --git a/images/wechat_group.jpg b/images/wechat_group.jpg index 1e6d37894b..1576b70b82 100644 Binary files a/images/wechat_group.jpg and b/images/wechat_group.jpg differ diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/dto/CreateFreeSwitchConferenceRequest.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/dto/CreateFreeSwitchConferenceRequest.java new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/dto/CreateFreeSwitchGatewayRequest.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/dto/CreateFreeSwitchGatewayRequest.java new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/dto/CreateFreeSwitchUserRequest.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/dto/CreateFreeSwitchUserRequest.java new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/dto/FreeSwitchCdrDto.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/dto/FreeSwitchCdrDto.java new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/dto/FreeSwitchConferenceDto.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/dto/FreeSwitchConferenceDto.java new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/dto/FreeSwitchDtoMapper.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/dto/FreeSwitchDtoMapper.java new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/dto/FreeSwitchGatewayDto.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/dto/FreeSwitchGatewayDto.java new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/dto/FreeSwitchUserDto.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/dto/FreeSwitchUserDto.java new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/model/FreeSwitchCdrEntity.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/model/FreeSwitchCdrEntity.java new file mode 100644 index 0000000000..440adfe870 --- /dev/null +++ b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/model/FreeSwitchCdrEntity.java @@ -0,0 +1,186 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2025-06-09 10:00:00 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-06-09 10:00: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. + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * + * Copyright (c) 2025 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.freeswitch.model; + +import java.time.LocalDateTime; + +import com.bytedesk.core.base.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import lombok.experimental.SuperBuilder; + +/** + * FreeSwitch通话详单记录实体 + * 对应数据库表:freeswitch_cdr + */ +@Entity +@Data +@SuperBuilder +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +@AllArgsConstructor +@NoArgsConstructor +@EntityListeners({FreeSwitchCdrEntityListener.class}) +@Table(name = "freeswitch_cdr") +public class FreeSwitchCdrEntity extends BaseEntity { + + /** + * 通话唯一标识符 + */ + @Column(name = "uuid", nullable = false, unique = true, length = 36) + private String uuid; + + /** + * 主叫名称 + */ + @Column(name = "caller_id_name", length = 100) + private String callerIdName; + + /** + * 主叫号码 + */ + @Column(name = "caller_id_number", nullable = false, length = 50) + private String callerIdNumber; + + /** + * 被叫号码 + */ + @Column(name = "destination_number", nullable = false, length = 50) + private String destinationNumber; + + /** + * 上下文 + */ + @Column(name = "context", length = 50) + private String context; + + /** + * 通话开始时间 + */ + @Column(name = "start_stamp") + private LocalDateTime startStamp; + + /** + * 通话接通时间 + */ + @Column(name = "answer_stamp") + private LocalDateTime answerStamp; + + /** + * 通话结束时间 + */ + @Column(name = "end_stamp") + private LocalDateTime endStamp; + + /** + * 通话总时长(秒) + */ + @Column(name = "duration") + private Integer duration; + + /** + * 计费时长(秒) + */ + @Column(name = "billsec") + private Integer billsec; + + /** + * 挂断原因 + */ + @Column(name = "hangup_cause", length = 50) + private String hangupCause; + + /** + * 账户代码 + */ + @Column(name = "accountcode", length = 50) + private String accountcode; + + /** + * 读取编解码器 + */ + @Column(name = "read_codec", length = 20) + private String readCodec; + + /** + * 写入编解码器 + */ + @Column(name = "write_codec", length = 20) + private String writeCodec; + + /** + * SIP挂断处理 + */ + @Column(name = "sip_hangup_disposition", length = 50) + private String sipHangupDisposition; + + /** + * 录音文件路径 + */ + @Column(name = "record_file", length = 500) + private String recordFile; + + /** + * 通话方向(inbound/outbound) + */ + @Column(name = "direction", length = 20) + private String direction; + + /** + * JSON格式的扩展信息 + */ + @Column(name = "json", columnDefinition = "TEXT") + private String json; + + /** + * 获取通话状态描述 + */ + public String getCallStatusDescription() { + if (answerStamp != null) { + return "已接通"; + } else if (hangupCause != null) { + return "未接通 - " + hangupCause; + } else { + return "未知状态"; + } + } + + /** + * 检查是否为成功通话 + */ + public boolean isSuccessfulCall() { + return answerStamp != null && billsec != null && billsec > 0; + } + + /** + * 获取格式化的通话时长 + */ + public String getFormattedDuration() { + if (duration == null) return "00:00:00"; + + int hours = duration / 3600; + int minutes = (duration % 3600) / 60; + int seconds = duration % 60; + + return String.format("%02d:%02d:%02d", hours, minutes, seconds); + } +} diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/model/FreeSwitchCdrEntityListener.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/model/FreeSwitchCdrEntityListener.java new file mode 100644 index 0000000000..bb52149e53 --- /dev/null +++ b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/model/FreeSwitchCdrEntityListener.java @@ -0,0 +1,34 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2025-06-09 10:00:00 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-06-09 10:00: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. + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * + * Copyright (c) 2025 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.freeswitch.model; + +import org.springframework.stereotype.Component; + +import jakarta.persistence.PostPersist; +import lombok.extern.slf4j.Slf4j; + +/** + * FreeSwitch CDR实体监听器 + */ +@Slf4j +@Component +public class FreeSwitchCdrEntityListener { + + @PostPersist + public void postPersist(FreeSwitchCdrEntity entity) { + log.info("FreeSwitch CDR记录创建: uuid={}, caller={}, destination={}, duration={}", + entity.getUuid(), entity.getCallerIdNumber(), + entity.getDestinationNumber(), entity.getDuration()); + } +} diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/model/FreeSwitchConferenceEntity.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/model/FreeSwitchConferenceEntity.java new file mode 100644 index 0000000000..3628365b17 --- /dev/null +++ b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/model/FreeSwitchConferenceEntity.java @@ -0,0 +1,117 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2025-06-09 10:00:00 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-06-09 10:00: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. + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * + * Copyright (c) 2025 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.freeswitch.model; + +import com.bytedesk.core.base.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import lombok.experimental.SuperBuilder; + +/** + * FreeSwitch会议室实体 + * 对应数据库表:freeswitch_conferences + */ +@Entity +@Data +@SuperBuilder +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +@AllArgsConstructor +@NoArgsConstructor +@EntityListeners({FreeSwitchConferenceEntityListener.class}) +@Table(name = "freeswitch_conferences") +public class FreeSwitchConferenceEntity extends BaseEntity { + + /** + * 会议室名称 + */ + @Column(name = "conference_name", nullable = false, unique = true, length = 100) + private String conferenceName; + + /** + * 会议室描述 + */ + @Column(name = "description", length = 255) + private String description; + + /** + * 会议室密码 + */ + @Column(name = "password", length = 50) + private String password; + + /** + * 最大参与者数量 + */ + @Column(name = "max_members") + private Integer maxMembers; + + /** + * 是否启用 + */ + @Column(name = "enabled", nullable = false) + private Boolean enabled = true; + + /** + * 是否录音 + */ + @Column(name = "record_enabled", nullable = false) + private Boolean recordEnabled = false; + + /** + * 录音文件路径 + */ + @Column(name = "record_path", length = 500) + private String recordPath; + + /** + * 创建者 + */ + @Column(name = "creator", length = 100) + private String creator; + + /** + * 会议室配置参数(JSON格式) + */ + @Column(name = "config_json", columnDefinition = "TEXT") + private String configJson; + + /** + * 备注 + */ + @Column(name = "remarks", length = 500) + private String remarks; + + /** + * 检查会议室是否有密码保护 + */ + public boolean isPasswordProtected() { + return password != null && !password.trim().isEmpty(); + } + + /** + * 检查会议室是否已满 + */ + public boolean isFull(int currentMembers) { + return maxMembers != null && currentMembers >= maxMembers; + } +} diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/model/FreeSwitchConferenceEntityListener.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/model/FreeSwitchConferenceEntityListener.java new file mode 100644 index 0000000000..b8c209a12c --- /dev/null +++ b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/model/FreeSwitchConferenceEntityListener.java @@ -0,0 +1,40 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2025-06-09 10:00:00 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-06-09 10:00: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. + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * + * Copyright (c) 2025 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.freeswitch.model; + +import org.springframework.stereotype.Component; + +import jakarta.persistence.PostPersist; +import jakarta.persistence.PostUpdate; +import lombok.extern.slf4j.Slf4j; + +/** + * FreeSwitch会议室实体监听器 + */ +@Slf4j +@Component +public class FreeSwitchConferenceEntityListener { + + @PostPersist + public void postPersist(FreeSwitchConferenceEntity entity) { + log.info("FreeSwitch会议室创建: name={}, maxMembers={}", + entity.getConferenceName(), entity.getMaxMembers()); + } + + @PostUpdate + public void postUpdate(FreeSwitchConferenceEntity entity) { + log.info("FreeSwitch会议室更新: name={}, enabled={}", + entity.getConferenceName(), entity.getEnabled()); + } +} diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/model/FreeSwitchGatewayEntity.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/model/FreeSwitchGatewayEntity.java new file mode 100644 index 0000000000..bf335159eb --- /dev/null +++ b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/model/FreeSwitchGatewayEntity.java @@ -0,0 +1,138 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2025-06-09 10:00:00 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-06-09 10:00: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. + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * + * Copyright (c) 2025 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.freeswitch.model; + +import com.bytedesk.core.base.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import lombok.experimental.SuperBuilder; + +/** + * FreeSwitch网关实体 + * 对应数据库表:freeswitch_gateways + */ +@Entity +@Data +@SuperBuilder +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +@AllArgsConstructor +@NoArgsConstructor +@EntityListeners({FreeSwitchGatewayEntityListener.class}) +@Table(name = "freeswitch_gateways") +public class FreeSwitchGatewayEntity extends BaseEntity { + + /** + * 网关名称 + */ + @Column(name = "gateway_name", nullable = false, unique = true, length = 100) + private String gatewayName; + + /** + * 网关描述 + */ + @Column(name = "description", length = 255) + private String description; + + /** + * SIP服务器地址 + */ + @Column(name = "proxy", nullable = false, length = 255) + private String proxy; + + /** + * 用户名 + */ + @Column(name = "username", nullable = false, length = 100) + private String username; + + /** + * 密码 + */ + @Column(name = "password", nullable = false, length = 255) + private String password; + + /** + * 从号码 + */ + @Column(name = "from_user", length = 100) + private String fromUser; + + /** + * 从域名 + */ + @Column(name = "from_domain", length = 100) + private String fromDomain; + + /** + * 注册 + */ + @Column(name = "register", nullable = false) + private Boolean register = true; + + /** + * 注册传输协议 + */ + @Column(name = "register_transport", length = 20) + private String registerTransport = "udp"; + + /** + * 网关状态 + */ + @Column(name = "status", length = 20) + private String status = "DOWN"; + + /** + * 是否启用 + */ + @Column(name = "enabled", nullable = false) + private Boolean enabled = true; + + /** + * 扩展配置(JSON格式) + */ + @Column(name = "config_json", columnDefinition = "TEXT") + private String configJson; + + /** + * 备注 + */ + @Column(name = "remarks", length = 500) + private String remarks; + + /** + * 检查网关是否在线 + */ + public boolean isOnline() { + return "UP".equalsIgnoreCase(status) && enabled; + } + + /** + * 获取完整的代理地址 + */ + public String getFullProxy() { + if (proxy.contains("sip:")) { + return proxy; + } + return "sip:" + proxy; + } +} diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/model/FreeSwitchGatewayEntityListener.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/model/FreeSwitchGatewayEntityListener.java new file mode 100644 index 0000000000..3beb0131f8 --- /dev/null +++ b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/model/FreeSwitchGatewayEntityListener.java @@ -0,0 +1,40 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2025-06-09 10:00:00 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-06-09 10:00: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. + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * + * Copyright (c) 2025 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.freeswitch.model; + +import org.springframework.stereotype.Component; + +import jakarta.persistence.PostPersist; +import jakarta.persistence.PostUpdate; +import lombok.extern.slf4j.Slf4j; + +/** + * FreeSwitch网关实体监听器 + */ +@Slf4j +@Component +public class FreeSwitchGatewayEntityListener { + + @PostPersist + public void postPersist(FreeSwitchGatewayEntity entity) { + log.info("FreeSwitch网关创建: name={}, proxy={}", + entity.getGatewayName(), entity.getProxy()); + } + + @PostUpdate + public void postUpdate(FreeSwitchGatewayEntity entity) { + log.info("FreeSwitch网关更新: name={}, status={}, enabled={}", + entity.getGatewayName(), entity.getStatus(), entity.getEnabled()); + } +} diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/model/FreeSwitchUserEntity.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/model/FreeSwitchUserEntity.java new file mode 100644 index 0000000000..1f7dea9634 --- /dev/null +++ b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/model/FreeSwitchUserEntity.java @@ -0,0 +1,126 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2025-06-09 10:00:00 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-06-09 10:00: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. + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * + * Copyright (c) 2025 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.freeswitch.model; + +import java.time.LocalDateTime; + +import com.bytedesk.core.base.BaseEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import lombok.experimental.SuperBuilder; + +/** + * FreeSwitch用户实体 + * 对应数据库表:freeswitch_users + */ +@Entity +@Data +@SuperBuilder +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +@AllArgsConstructor +@NoArgsConstructor +@EntityListeners({FreeSwitchUserEntityListener.class}) +@Table(name = "freeswitch_users") +public class FreeSwitchUserEntity extends BaseEntity { + + /** + * 用户名(SIP用户名) + */ + @Column(name = "username", nullable = false, unique = true, length = 50) + private String username; + + /** + * SIP域名 + */ + @Column(name = "domain", nullable = false, length = 100) + private String domain; + + /** + * 密码 + */ + @Column(name = "password", nullable = false, length = 255) + private String password; + + /** + * 显示名称 + */ + @Column(name = "display_name", length = 100) + private String displayName; + + /** + * 邮箱 + */ + @Column(name = "email", length = 100) + private String email; + + /** + * 账户代码 + */ + @Column(name = "accountcode", length = 50) + private String accountcode; + + /** + * 是否启用 + */ + @Column(name = "enabled", nullable = false) + private Boolean enabled = true; + + /** + * 最后注册时间 + */ + @Column(name = "last_register") + private LocalDateTime lastRegister; + + /** + * 注册IP地址 + */ + @Column(name = "register_ip", length = 45) + private String registerIp; + + /** + * 用户代理 + */ + @Column(name = "user_agent", length = 255) + private String userAgent; + + /** + * 备注 + */ + @Column(name = "remarks", length = 500) + private String remarks; + + /** + * 获取完整的SIP地址 + */ + public String getSipAddress() { + return username + "@" + domain; + } + + /** + * 检查用户是否在线 + */ + public boolean isOnline() { + return enabled && lastRegister != null && + lastRegister.isAfter(LocalDateTime.now().minusMinutes(5)); + } +} diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/model/FreeSwitchUserEntityListener.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/model/FreeSwitchUserEntityListener.java new file mode 100644 index 0000000000..ec5f80f190 --- /dev/null +++ b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/model/FreeSwitchUserEntityListener.java @@ -0,0 +1,40 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2025-06-09 10:00:00 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-06-09 10:00: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. + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * + * Copyright (c) 2025 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.freeswitch.model; + +import org.springframework.stereotype.Component; + +import jakarta.persistence.PostPersist; +import jakarta.persistence.PostUpdate; +import lombok.extern.slf4j.Slf4j; + +/** + * FreeSwitch用户实体监听器 + */ +@Slf4j +@Component +public class FreeSwitchUserEntityListener { + + @PostPersist + public void postPersist(FreeSwitchUserEntity entity) { + log.info("FreeSwitch用户创建: username={}, domain={}", + entity.getUsername(), entity.getDomain()); + } + + @PostUpdate + public void postUpdate(FreeSwitchUserEntity entity) { + log.info("FreeSwitch用户更新: username={}, enabled={}", + entity.getUsername(), entity.getEnabled()); + } +} diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/repository/FreeSwitchCdrRepository.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/repository/FreeSwitchCdrRepository.java new file mode 100644 index 0000000000..bceca7a511 --- /dev/null +++ b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/repository/FreeSwitchCdrRepository.java @@ -0,0 +1,139 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2025-06-09 10:00:00 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-06-09 10:00: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. + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * + * Copyright (c) 2025 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.freeswitch.repository; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import com.bytedesk.freeswitch.model.FreeSwitchCdrEntity; + +/** + * FreeSwitch CDR仓库接口 + */ +@Repository +public interface FreeSwitchCdrRepository extends JpaRepository, + JpaSpecificationExecutor { + + /** + * 根据UUID查找CDR记录 + */ + Optional findByUuid(String uuid); + + /** + * 根据主叫号码查找CDR记录 + */ + List findByCallerIdNumber(String callerIdNumber); + + /** + * 根据被叫号码查找CDR记录 + */ + List findByDestinationNumber(String destinationNumber); + + /** + * 根据账户代码查找CDR记录 + */ + List findByAccountcode(String accountcode); + + /** + * 根据通话方向查找CDR记录 + */ + List findByDirection(String direction); + + /** + * 根据挂断原因查找CDR记录 + */ + List findByHangupCause(String hangupCause); + + /** + * 查找指定时间范围内的CDR记录 + */ + List findByStartStampBetween(LocalDateTime startTime, LocalDateTime endTime); + + /** + * 查找成功接通的通话记录 + */ + List findByAnswerStampIsNotNull(); + + /** + * 查找有录音文件的通话记录 + */ + List findByRecordFileIsNotNull(); + + /** + * 根据主叫或被叫号码查找CDR记录 + */ + @Query("SELECT c FROM FreeSwitchCdrEntity c WHERE c.callerIdNumber = :number OR c.destinationNumber = :number") + List findByCallerOrDestination(@Param("number") String number); + + /** + * 查找指定号码的通话历史(分页) + */ + @Query("SELECT c FROM FreeSwitchCdrEntity c WHERE c.callerIdNumber = :number OR c.destinationNumber = :number ORDER BY c.startStamp DESC") + Page findCallHistory(@Param("number") String number, Pageable pageable); + + /** + * 统计指定时间范围内的通话数量 + */ + @Query("SELECT COUNT(c) FROM FreeSwitchCdrEntity c WHERE c.startStamp BETWEEN :startTime AND :endTime") + long countCallsInTimeRange(@Param("startTime") LocalDateTime startTime, @Param("endTime") LocalDateTime endTime); + + /** + * 统计指定时间范围内成功接通的通话数量 + */ + @Query("SELECT COUNT(c) FROM FreeSwitchCdrEntity c WHERE c.startStamp BETWEEN :startTime AND :endTime AND c.answerStamp IS NOT NULL") + long countAnsweredCallsInTimeRange(@Param("startTime") LocalDateTime startTime, @Param("endTime") LocalDateTime endTime); + + /** + * 计算指定时间范围内的总通话时长 + */ + @Query("SELECT COALESCE(SUM(c.billsec), 0) FROM FreeSwitchCdrEntity c WHERE c.startStamp BETWEEN :startTime AND :endTime AND c.answerStamp IS NOT NULL") + long sumBillSecInTimeRange(@Param("startTime") LocalDateTime startTime, @Param("endTime") LocalDateTime endTime); + + /** + * 查找最近的通话记录 + */ + @Query("SELECT c FROM FreeSwitchCdrEntity c ORDER BY c.startStamp DESC") + Page findRecentCalls(Pageable pageable); + + /** + * 根据主叫号码统计通话次数 + */ + @Query("SELECT COUNT(c) FROM FreeSwitchCdrEntity c WHERE c.callerIdNumber = :callerNumber") + long countByCallerNumber(@Param("callerNumber") String callerNumber); + + /** + * 根据被叫号码统计通话次数 + */ + @Query("SELECT COUNT(c) FROM FreeSwitchCdrEntity c WHERE c.destinationNumber = :destinationNumber") + long countByDestinationNumber(@Param("destinationNumber") String destinationNumber); + + /** + * 检查UUID是否存在 + */ + boolean existsByUuid(String uuid); + + /** + * 删除指定日期之前的CDR记录 + */ + void deleteByStartStampBefore(LocalDateTime cutoffDate); +} diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/repository/FreeSwitchConferenceRepository.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/repository/FreeSwitchConferenceRepository.java new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/repository/FreeSwitchGatewayRepository.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/repository/FreeSwitchGatewayRepository.java new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/repository/FreeSwitchUserRepository.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/repository/FreeSwitchUserRepository.java new file mode 100644 index 0000000000..bf104135fa --- /dev/null +++ b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/repository/FreeSwitchUserRepository.java @@ -0,0 +1,112 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2025-06-09 10:00:00 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-06-09 10:00: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. + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * + * Copyright (c) 2025 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.freeswitch.repository; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import com.bytedesk.freeswitch.model.FreeSwitchUserEntity; + +/** + * FreeSwitch用户仓库接口 + */ +@Repository +public interface FreeSwitchUserRepository extends JpaRepository, + JpaSpecificationExecutor { + + /** + * 根据用户名查找用户 + */ + Optional findByUsername(String username); + + /** + * 根据用户名和域名查找用户 + */ + Optional findByUsernameAndDomain(String username, String domain); + + /** + * 根据邮箱查找用户 + */ + Optional findByEmail(String email); + + /** + * 查找启用的用户 + */ + List findByEnabledTrue(); + + /** + * 根据域名查找用户 + */ + List findByDomain(String domain); + + /** + * 根据账户代码查找用户 + */ + List findByAccountcode(String accountcode); + + /** + * 检查用户名是否存在 + */ + boolean existsByUsername(String username); + + /** + * 检查用户名和域名组合是否存在 + */ + boolean existsByUsernameAndDomain(String username, String domain); + + /** + * 查找在线用户(最近5分钟内有注册记录) + */ + @Query("SELECT u FROM FreeSwitchUserEntity u WHERE u.enabled = true AND u.lastRegister > :cutoffTime") + List findOnlineUsers(@Param("cutoffTime") LocalDateTime cutoffTime); + + /** + * 根据用户名模糊搜索 + */ + Page findByUsernameContainingIgnoreCase(String username, Pageable pageable); + + /** + * 根据显示名称模糊搜索 + */ + Page findByDisplayNameContainingIgnoreCase(String displayName, Pageable pageable); + + /** + * 统计启用的用户数量 + */ + long countByEnabledTrue(); + + /** + * 统计在线用户数量 + */ + @Query("SELECT COUNT(u) FROM FreeSwitchUserEntity u WHERE u.enabled = true AND u.lastRegister > :cutoffTime") + long countOnlineUsers(@Param("cutoffTime") LocalDateTime cutoffTime); + + /** + * 更新用户最后注册时间和IP + */ + @Query("UPDATE FreeSwitchUserEntity u SET u.lastRegister = :registerTime, u.registerIp = :registerIp WHERE u.username = :username AND u.domain = :domain") + int updateLastRegister(@Param("username") String username, + @Param("domain") String domain, + @Param("registerTime") LocalDateTime registerTime, + @Param("registerIp") String registerIp); +} diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/service/FreeSwitchCdrService.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/service/FreeSwitchCdrService.java new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/service/FreeSwitchConferenceService.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/service/FreeSwitchConferenceService.java new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/service/FreeSwitchGatewayService.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/service/FreeSwitchGatewayService.java new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/service/FreeSwitchUserService.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/service/FreeSwitchUserService.java new file mode 100644 index 0000000000..e69de29bb2