diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/cdr/FreeSwitchCdrExcel.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/cdr/FreeSwitchCdrExcel.java new file mode 100644 index 0000000000..699c7916fb --- /dev/null +++ b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/cdr/FreeSwitchCdrExcel.java @@ -0,0 +1,87 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2025-06-08 10:00:00 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-06-08 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.cdr; + +import java.time.LocalDateTime; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.format.DateTimeFormat; +import com.alibaba.excel.annotation.write.style.ColumnWidth; + +import lombok.Data; + +/** + * FreeSwitch通话详单Excel导出类 + * https://github.com/alibaba/easyexcel + */ +@Data +public class FreeSwitchCdrExcel { + + @ExcelProperty(index = 0, value = "通话UUID") + @ColumnWidth(40) + private String uuid; + + @ExcelProperty(index = 1, value = "主叫号码") + @ColumnWidth(20) + private String callerIdNumber; + + @ExcelProperty(index = 2, value = "被叫号码") + @ColumnWidth(20) + private String destinationNumber; + + @ExcelProperty(index = 3, value = "通话状态") + @ColumnWidth(15) + private String hangupCause; + + @ExcelProperty(index = 4, value = "通话时长(秒)") + @ColumnWidth(15) + private Integer duration; + + @ExcelProperty(index = 5, value = "计费时长(秒)") + @ColumnWidth(15) + private Integer billSec; + + @ExcelProperty(index = 6, value = "接听延迟(秒)") + @ColumnWidth(15) + private Integer progressMediaSec; + + @ExcelProperty(index = 7, value = "主叫IP") + @ColumnWidth(20) + private String networkAddr; + + @ExcelProperty(index = 8, value = "用户代理") + @ColumnWidth(30) + private String userAgent; + + @DateTimeFormat("yyyy-MM-dd HH:mm:ss") + @ExcelProperty(value = "开始时间") + @ColumnWidth(25) + private LocalDateTime startStamp; + + @DateTimeFormat("yyyy-MM-dd HH:mm:ss") + @ExcelProperty(value = "接听时间") + @ColumnWidth(25) + private LocalDateTime answerStamp; + + @DateTimeFormat("yyyy-MM-dd HH:mm:ss") + @ExcelProperty(value = "结束时间") + @ColumnWidth(25) + private LocalDateTime endStamp; + + @DateTimeFormat("yyyy-MM-dd HH:mm:ss") + @ExcelProperty(value = "创建时间") + @ColumnWidth(25) + private LocalDateTime createdAt; + +} diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/cdr/FreeSwitchCdrRequest.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/cdr/FreeSwitchCdrRequest.java new file mode 100644 index 0000000000..b48fcef011 --- /dev/null +++ b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/cdr/FreeSwitchCdrRequest.java @@ -0,0 +1,82 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2025-06-08 10:00:00 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-06-08 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.cdr; + +import com.bytedesk.core.base.BaseRequest; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import lombok.experimental.SuperBuilder; + +import java.time.LocalDateTime; + +/** + * FreeSwitch通话详单请求实体 + */ +@Data +@SuperBuilder +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +@AllArgsConstructor +@NoArgsConstructor +public class FreeSwitchCdrRequest extends BaseRequest { + + /** + * 通话UUID + */ + private String uuid; + + /** + * 主叫号码 + */ + private String callerIdNumber; + + /** + * 被叫号码 + */ + private String destinationNumber; + + /** + * 挂断原因 + */ + private String hangupCause; + + /** + * 主叫IP地址 + */ + private String networkAddr; + + /** + * 最小通话时长 + */ + private Integer minDuration; + + /** + * 最大通话时长 + */ + private Integer maxDuration; + + /** + * 开始日期 + */ + private LocalDateTime startDate; + + /** + * 结束日期 + */ + private LocalDateTime endDate; + +} diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/cdr/FreeSwitchCdrResponse.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/cdr/FreeSwitchCdrResponse.java new file mode 100644 index 0000000000..c11713beb6 --- /dev/null +++ b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/cdr/FreeSwitchCdrResponse.java @@ -0,0 +1,122 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2025-06-08 10:00:00 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-06-08 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.cdr; + +import com.bytedesk.core.base.BaseResponse; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import lombok.experimental.SuperBuilder; + +import java.time.LocalDateTime; + +/** + * FreeSwitch通话详单响应实体 + */ +@Data +@SuperBuilder +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +@AllArgsConstructor +@NoArgsConstructor +public class FreeSwitchCdrResponse extends BaseResponse { + + /** + * 通话UUID + */ + private String uuid; + + /** + * 主叫号码名称 + */ + private String callerIdName; + + /** + * 主叫号码 + */ + private String callerIdNumber; + + /** + * 被叫号码 + */ + private String destinationNumber; + + /** + * 用户上下文 + */ + private String context; + + /** + * 挂断原因 + */ + private String hangupCause; + + /** + * 账户代码 + */ + private String accountCode; + + /** + * 通话时长(秒) + */ + private Integer duration; + + /** + * 计费时长(秒) + */ + private Integer billSec; + + /** + * 铃声时长(秒) + */ + private Integer progressSec; + + /** + * 振铃时长(秒) + */ + private Integer progressMediaSec; + + /** + * 开始时间戳 + */ + private LocalDateTime startStamp; + + /** + * 接听时间戳 + */ + private LocalDateTime answerStamp; + + /** + * 结束时间戳 + */ + private LocalDateTime endStamp; + + /** + * 主叫IP地址 + */ + private String networkAddr; + + /** + * 用户代理 + */ + private String userAgent; + + /** + * 录音文件路径 + */ + private String recordFile; + +} diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/cdr/FreeSwitchCdrRestController.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/cdr/FreeSwitchCdrRestController.java new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/cdr/FreeSwitchCdrRestService.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/cdr/FreeSwitchCdrRestService.java new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/cdr/FreeSwitchCdrSpecification.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/cdr/FreeSwitchCdrSpecification.java new file mode 100644 index 0000000000..d374bcb3b9 --- /dev/null +++ b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/cdr/FreeSwitchCdrSpecification.java @@ -0,0 +1,82 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2025-06-08 10:00:00 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-06-08 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.cdr; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +import org.springframework.data.jpa.domain.Specification; +import org.springframework.util.StringUtils; + +import com.bytedesk.core.base.BaseSpecification; + +import jakarta.persistence.criteria.Predicate; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class FreeSwitchCdrSpecification extends BaseSpecification { + + public static Specification search(FreeSwitchCdrRequest request) { + log.info("request: {}", request); + return (root, query, criteriaBuilder) -> { + List predicates = new ArrayList<>(); + predicates.addAll(getBasicPredicates(root, criteriaBuilder, request.getOrgUid())); + + // uuid + if (StringUtils.hasText(request.getUuid())) { + predicates.add(criteriaBuilder.equal(root.get("uuid"), request.getUuid())); + } + + // callerIdNumber + if (StringUtils.hasText(request.getCallerIdNumber())) { + predicates.add(criteriaBuilder.like(root.get("callerIdNumber"), "%" + request.getCallerIdNumber() + "%")); + } + + // destinationNumber + if (StringUtils.hasText(request.getDestinationNumber())) { + predicates.add(criteriaBuilder.like(root.get("destinationNumber"), "%" + request.getDestinationNumber() + "%")); + } + + // hangupCause + if (StringUtils.hasText(request.getHangupCause())) { + predicates.add(criteriaBuilder.equal(root.get("hangupCause"), request.getHangupCause())); + } + + // networkAddr + if (StringUtils.hasText(request.getNetworkAddr())) { + predicates.add(criteriaBuilder.like(root.get("networkAddr"), "%" + request.getNetworkAddr() + "%")); + } + + // duration range + if (request.getMinDuration() != null) { + predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get("duration"), request.getMinDuration())); + } + if (request.getMaxDuration() != null) { + predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get("duration"), request.getMaxDuration())); + } + + // date range + if (request.getStartDate() != null) { + predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get("startStamp"), request.getStartDate())); + } + if (request.getEndDate() != null) { + predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get("endStamp"), request.getEndDate())); + } + + return criteriaBuilder.and(predicates.toArray(new Predicate[0])); + }; + } + +} diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/conference/FreeSwitchConferenceExcel.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/conference/FreeSwitchConferenceExcel.java new file mode 100644 index 0000000000..2c51b3e9fc --- /dev/null +++ b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/conference/FreeSwitchConferenceExcel.java @@ -0,0 +1,73 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2025-06-08 10:00:00 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-06-08 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.conference; + +import java.time.LocalDateTime; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.format.DateTimeFormat; +import com.alibaba.excel.annotation.write.style.ColumnWidth; + +import lombok.Data; + +/** + * FreeSwitch会议室Excel导出类 + * https://github.com/alibaba/easyexcel + */ +@Data +public class FreeSwitchConferenceExcel { + + @ExcelProperty(index = 0, value = "会议室名称") + @ColumnWidth(20) + private String conferenceName; + + @ExcelProperty(index = 1, value = "描述") + @ColumnWidth(30) + private String description; + + @ExcelProperty(index = 2, value = "最大成员数") + @ColumnWidth(15) + private Integer maxMembers; + + @ExcelProperty(index = 3, value = "是否启用") + @ColumnWidth(15) + private Boolean enabled; + + @ExcelProperty(index = 4, value = "是否录音") + @ColumnWidth(15) + private Boolean recordEnabled; + + @ExcelProperty(index = 5, value = "是否有密码") + @ColumnWidth(15) + private Boolean passwordProtected; + + @ExcelProperty(index = 6, value = "主持人PIN") + @ColumnWidth(20) + private String moderatorPin; + + @ExcelProperty(index = 7, value = "参会者PIN") + @ColumnWidth(20) + private String participantPin; + + @DateTimeFormat("yyyy-MM-dd HH:mm:ss") + @ExcelProperty(value = "创建时间") + @ColumnWidth(25) + private LocalDateTime createdAt; + + @DateTimeFormat("yyyy-MM-dd HH:mm:ss") + @ExcelProperty(value = "修改时间") + @ColumnWidth(25) + private LocalDateTime updatedAt; + +} diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/conference/FreeSwitchConferenceRestController.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/conference/FreeSwitchConferenceRestController.java new file mode 100644 index 0000000000..d817cd6e25 --- /dev/null +++ b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/conference/FreeSwitchConferenceRestController.java @@ -0,0 +1,99 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2025-06-08 10:00:00 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-06-08 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.conference; + +import org.springframework.data.domain.Page; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.bytedesk.core.annotation.ActionAnnotation; +import com.bytedesk.core.base.BaseRestController; +import com.bytedesk.core.utils.JsonResult; + +import jakarta.servlet.http.HttpServletResponse; +import lombok.AllArgsConstructor; + +@RestController +@RequestMapping("/api/v1/freeswitch/conference") +@AllArgsConstructor +public class FreeSwitchConferenceRestController extends BaseRestController { + + private final FreeSwitchConferenceRestService freeSwitchConferenceRestService; + + @ActionAnnotation(title = "FreeSwitch会议室", action = "组织查询", description = "query freeswitch conference by org") + @Override + public ResponseEntity queryByOrg(FreeSwitchConferenceRequest request) { + + Page conferences = freeSwitchConferenceRestService.queryByOrg(request); + + return ResponseEntity.ok(JsonResult.success(conferences)); + } + + @ActionAnnotation(title = "FreeSwitch会议室", action = "用户查询", description = "query freeswitch conference by user") + @Override + public ResponseEntity queryByUser(FreeSwitchConferenceRequest request) { + + Page conferences = freeSwitchConferenceRestService.queryByUser(request); + + return ResponseEntity.ok(JsonResult.success(conferences)); + } + + @ActionAnnotation(title = "FreeSwitch会议室", action = "查询", description = "query freeswitch conference by uid") + @Override + public ResponseEntity queryByUid(FreeSwitchConferenceRequest request) { + + FreeSwitchConferenceResponse conference = freeSwitchConferenceRestService.queryByUid(request); + + return ResponseEntity.ok(JsonResult.success(conference)); + } + + @ActionAnnotation(title = "FreeSwitch会议室", action = "创建", description = "create freeswitch conference") + @Override + public ResponseEntity create(FreeSwitchConferenceRequest request) { + + FreeSwitchConferenceResponse conference = freeSwitchConferenceRestService.create(request); + + return ResponseEntity.ok(JsonResult.success(conference)); + } + + @ActionAnnotation(title = "FreeSwitch会议室", action = "更新", description = "update freeswitch conference") + @Override + public ResponseEntity update(FreeSwitchConferenceRequest request) { + + FreeSwitchConferenceResponse conference = freeSwitchConferenceRestService.update(request); + + return ResponseEntity.ok(JsonResult.success(conference)); + } + + @ActionAnnotation(title = "FreeSwitch会议室", action = "删除", description = "delete freeswitch conference") + @Override + public ResponseEntity delete(FreeSwitchConferenceRequest request) { + + freeSwitchConferenceRestService.deleteByUid(request.getUid()); + + return ResponseEntity.ok(JsonResult.success("删除成功", request.getUid())); + } + + @ActionAnnotation(title = "FreeSwitch会议室", action = "导出", description = "export freeswitch conference to excel") + @GetMapping("/export") + public ResponseEntity export(FreeSwitchConferenceRequest request, HttpServletResponse response) { + + freeSwitchConferenceRestService.export(request, response); + + return ResponseEntity.ok(JsonResult.success("导出成功")); + } + +} diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/conference/FreeSwitchConferenceRestService.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/conference/FreeSwitchConferenceRestService.java new file mode 100644 index 0000000000..2aeb352061 --- /dev/null +++ b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/conference/FreeSwitchConferenceRestService.java @@ -0,0 +1,182 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2025-06-08 10:00:00 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-06-08 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.conference; + +import java.util.Optional; + +import org.modelmapper.ModelMapper; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.orm.ObjectOptimisticLockingFailureException; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +import com.bytedesk.core.base.BaseRestServiceWithExcel; +import com.bytedesk.core.constant.BytedeskConsts; +import com.bytedesk.core.enums.LevelEnum; +import com.bytedesk.core.rbac.auth.AuthService; +import com.bytedesk.core.rbac.user.UserEntity; +import com.bytedesk.core.uid.UidUtils; +import com.bytedesk.core.utils.Utils; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Service +@AllArgsConstructor +public class FreeSwitchConferenceRestService extends BaseRestServiceWithExcel { + + private final FreeSwitchConferenceRepository freeSwitchConferenceRepository; + + private final ModelMapper modelMapper; + + private final UidUtils uidUtils; + + private final AuthService authService; + + @Override + public Page queryByOrgEntity(FreeSwitchConferenceRequest request) { + + Pageable pageable = Utils.getPageable(request); + Specification specification = FreeSwitchConferenceSpecification.search(request); + + return freeSwitchConferenceRepository.findAll(specification, pageable); + } + + @Override + public Page queryByUserEntity(FreeSwitchConferenceRequest request) { + + UserEntity user = authService.getCurrentUser(); + request.setOrgUid(user.getOrgUid()); + + return queryByOrgEntity(request); + } + + @Override + public Optional findByUid(String uid) { + return freeSwitchConferenceRepository.findByUidAndDeleted(uid, false); + } + + @Override + public FreeSwitchConferenceResponse convertToResponse(FreeSwitchConferenceEntity entity) { + return modelMapper.map(entity, FreeSwitchConferenceResponse.class); + } + + @Override + public FreeSwitchConferenceEntity convertToEntity(FreeSwitchConferenceRequest request) { + return modelMapper.map(request, FreeSwitchConferenceEntity.class); + } + + @Override + public FreeSwitchConferenceExcel convertToExcel(FreeSwitchConferenceEntity entity) { + return modelMapper.map(entity, FreeSwitchConferenceExcel.class); + } + + @Override + public FreeSwitchConferenceResponse create(FreeSwitchConferenceRequest request) { + + UserEntity user = authService.getCurrentUser(); + if (StringUtils.hasText(request.getOrgUid())) { + request.setOrgUid(user.getOrgUid()); + } + + if (StringUtils.hasText(request.getUid())) { + request.setUid(uidUtils.getCacheSerialUid()); + } + + // 检查会议室名称是否已存在 + if (freeSwitchConferenceRepository.existsByConferenceName(request.getConferenceName())) { + throw new RuntimeException("会议室名称已存在: " + request.getConferenceName()); + } + + FreeSwitchConferenceEntity entity = convertToEntity(request); + entity.setLevel(LevelEnum.PLATFORM.name()); + entity.setPlatform(BytedeskConsts.PLATFORM_BYTEDESK); + + FreeSwitchConferenceEntity savedEntity = save(entity); + if (savedEntity == null) { + throw new RuntimeException("创建FreeSwitch会议室失败"); + } + + return convertToResponse(savedEntity); + } + + @Override + public FreeSwitchConferenceResponse update(FreeSwitchConferenceRequest request) { + + Optional optional = findByUid(request.getUid()); + if (!optional.isPresent()) { + throw new RuntimeException("FreeSwitch会议室不存在"); + } + + FreeSwitchConferenceEntity entity = optional.get(); + + // 更新字段 + if (StringUtils.hasText(request.getDescription())) { + entity.setDescription(request.getDescription()); + } + if (StringUtils.hasText(request.getPassword())) { + entity.setPassword(request.getPassword()); + } + if (request.getMaxMembers() != null) { + entity.setMaxMembers(request.getMaxMembers()); + } + if (request.getEnabled() != null) { + entity.setEnabled(request.getEnabled()); + } + if (request.getRecordEnabled() != null) { + entity.setRecordEnabled(request.getRecordEnabled()); + } + + FreeSwitchConferenceEntity updatedEntity = save(entity); + if (updatedEntity == null) { + throw new RuntimeException("更新FreeSwitch会议室失败"); + } + + return convertToResponse(updatedEntity); + } + + @Override + public FreeSwitchConferenceEntity save(FreeSwitchConferenceEntity entity) { + try { + return freeSwitchConferenceRepository.save(entity); + } catch (ObjectOptimisticLockingFailureException e) { + handleOptimisticLockingFailureException(e, entity); + } + return null; + } + + @Override + public void deleteByUid(String uid) { + Optional optional = findByUid(uid); + optional.ifPresent(entity -> { + entity.setDeleted(true); + save(entity); + }); + } + + @Override + @Cacheable(value = "freeswitch_conference", key = "#uid", unless = "#result == null") + public FreeSwitchConferenceResponse queryByUid(FreeSwitchConferenceRequest request) { + Optional optional = findByUid(request.getUid()); + if (optional.isPresent()) { + return convertToResponse(optional.get()); + } + throw new RuntimeException("FreeSwitch会议室不存在"); + } + +} diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/gateway/FreeSwitchGatewayExcel.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/gateway/FreeSwitchGatewayExcel.java new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/gateway/FreeSwitchGatewayResponse.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/gateway/FreeSwitchGatewayResponse.java index b732708889..107a2deaad 100644 --- a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/gateway/FreeSwitchGatewayResponse.java +++ b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/gateway/FreeSwitchGatewayResponse.java @@ -13,26 +13,23 @@ */ package com.bytedesk.freeswitch.gateway; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; +import com.bytedesk.core.base.BaseResponse; -import java.time.LocalDateTime; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; /** * FreeSwitch网关响应实体 */ @Data -@Builder +@SuperBuilder @NoArgsConstructor @AllArgsConstructor -public class FreeSwitchGatewayResponse { - - /** - * ID - */ - private Long id; +@EqualsAndHashCode(callSuper = true) +public class FreeSwitchGatewayResponse extends BaseResponse { /** * 网关名称 diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/gateway/FreeSwitchGatewaySpecification.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/gateway/FreeSwitchGatewaySpecification.java new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/user/FreeSwitchUserController.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/user/FreeSwitchUserController.java index e54852913c..acf65c7b82 100644 --- a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/user/FreeSwitchUserController.java +++ b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/user/FreeSwitchUserController.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2025-06-09 10:00:00 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2025-06-09 10:00:00 + * @LastEditTime: 2025-06-08 18:11:05 * @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. diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/user/FreeSwitchUserExcel.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/user/FreeSwitchUserExcel.java index e69de29bb2..475cfbd57b 100644 --- a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/user/FreeSwitchUserExcel.java +++ b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/user/FreeSwitchUserExcel.java @@ -0,0 +1,74 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2025-06-08 10:00:00 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-06-08 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.user; + +import java.time.LocalDateTime; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.format.DateTimeFormat; +import com.alibaba.excel.annotation.write.style.ColumnWidth; + +import lombok.Data; + +/** + * FreeSwitch用户Excel导出类 + * https://github.com/alibaba/easyexcel + */ +@Data +public class FreeSwitchUserExcel { + + @ExcelProperty(index = 0, value = "用户名") + @ColumnWidth(20) + private String username; + + @ExcelProperty(index = 1, value = "域名") + @ColumnWidth(20) + private String domain; + + @ExcelProperty(index = 2, value = "显示名称") + @ColumnWidth(20) + private String displayName; + + @ExcelProperty(index = 3, value = "邮箱") + @ColumnWidth(25) + private String email; + + @ExcelProperty(index = 4, value = "账户代码") + @ColumnWidth(20) + private String accountcode; + + @ExcelProperty(index = 5, value = "是否启用") + @ColumnWidth(15) + private Boolean enabled; + + @ExcelProperty(index = 6, value = "SIP端口") + @ColumnWidth(15) + private Integer sipPort; + + @DateTimeFormat("yyyy-MM-dd HH:mm:ss") + @ExcelProperty(value = "最后注册时间") + @ColumnWidth(25) + private LocalDateTime lastRegister; + + @DateTimeFormat("yyyy-MM-dd HH:mm:ss") + @ExcelProperty(value = "创建时间") + @ColumnWidth(25) + private LocalDateTime createdAt; + + @DateTimeFormat("yyyy-MM-dd HH:mm:ss") + @ExcelProperty(value = "修改时间") + @ColumnWidth(25) + private LocalDateTime updatedAt; + +} diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/user/FreeSwitchUserRestController.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/user/FreeSwitchUserRestController.java index e69de29bb2..564b32ea45 100644 --- a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/user/FreeSwitchUserRestController.java +++ b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/user/FreeSwitchUserRestController.java @@ -0,0 +1,99 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2025-06-08 10:00:00 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-06-08 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.user; + +import org.springframework.data.domain.Page; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.bytedesk.core.annotation.ActionAnnotation; +import com.bytedesk.core.base.BaseRestController; +import com.bytedesk.core.utils.JsonResult; + +import jakarta.servlet.http.HttpServletResponse; +import lombok.AllArgsConstructor; + +@RestController +@RequestMapping("/api/v1/freeswitch/user") +@AllArgsConstructor +public class FreeSwitchUserRestController extends BaseRestController { + + private final FreeSwitchUserRestService freeSwitchUserRestService; + + @ActionAnnotation(title = "FreeSwitch用户", action = "组织查询", description = "query freeswitch user by org") + @Override + public ResponseEntity queryByOrg(FreeSwitchUserRequest request) { + + Page users = freeSwitchUserRestService.queryByOrg(request); + + return ResponseEntity.ok(JsonResult.success(users)); + } + + @ActionAnnotation(title = "FreeSwitch用户", action = "用户查询", description = "query freeswitch user by user") + @Override + public ResponseEntity queryByUser(FreeSwitchUserRequest request) { + + Page users = freeSwitchUserRestService.queryByUser(request); + + return ResponseEntity.ok(JsonResult.success(users)); + } + + @ActionAnnotation(title = "FreeSwitch用户", action = "查询", description = "query freeswitch user by uid") + @Override + public ResponseEntity queryByUid(FreeSwitchUserRequest request) { + + FreeSwitchUserResponse user = freeSwitchUserRestService.queryByUid(request); + + return ResponseEntity.ok(JsonResult.success(user)); + } + + @ActionAnnotation(title = "FreeSwitch用户", action = "创建", description = "create freeswitch user") + @Override + public ResponseEntity create(FreeSwitchUserRequest request) { + + FreeSwitchUserResponse user = freeSwitchUserRestService.create(request); + + return ResponseEntity.ok(JsonResult.success(user)); + } + + @ActionAnnotation(title = "FreeSwitch用户", action = "更新", description = "update freeswitch user") + @Override + public ResponseEntity update(FreeSwitchUserRequest request) { + + FreeSwitchUserResponse user = freeSwitchUserRestService.update(request); + + return ResponseEntity.ok(JsonResult.success(user)); + } + + @ActionAnnotation(title = "FreeSwitch用户", action = "删除", description = "delete freeswitch user") + @Override + public ResponseEntity delete(FreeSwitchUserRequest request) { + + freeSwitchUserRestService.deleteByUid(request.getUid()); + + return ResponseEntity.ok(JsonResult.success("删除成功", request.getUid())); + } + + @ActionAnnotation(title = "FreeSwitch用户", action = "导出", description = "export freeswitch user to excel") + @GetMapping("/export") + public ResponseEntity export(FreeSwitchUserRequest request, HttpServletResponse response) { + + freeSwitchUserRestService.export(request, response); + + return ResponseEntity.ok(JsonResult.success("导出成功")); + } + +} diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/user/FreeSwitchUserRestService.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/user/FreeSwitchUserRestService.java index e69de29bb2..5802d4610a 100644 --- a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/user/FreeSwitchUserRestService.java +++ b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/user/FreeSwitchUserRestService.java @@ -0,0 +1,182 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2025-06-08 10:00:00 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-06-08 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.user; + +import java.util.Optional; + +import org.modelmapper.ModelMapper; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.orm.ObjectOptimisticLockingFailureException; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +import com.bytedesk.core.base.BaseRestServiceWithExcel; +import com.bytedesk.core.constant.BytedeskConsts; +import com.bytedesk.core.enums.LevelEnum; +import com.bytedesk.core.rbac.auth.AuthService; +import com.bytedesk.core.rbac.user.UserEntity; +import com.bytedesk.core.uid.UidUtils; +import com.bytedesk.core.utils.Utils; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Service +@AllArgsConstructor +public class FreeSwitchUserRestService extends BaseRestServiceWithExcel { + + private final FreeSwitchUserRepository freeSwitchUserRepository; + + private final ModelMapper modelMapper; + + private final UidUtils uidUtils; + + private final AuthService authService; + + @Override + public Page queryByOrgEntity(FreeSwitchUserRequest request) { + + Pageable pageable = Utils.getPageable(request); + Specification specification = FreeSwitchUserSpecification.search(request); + + return freeSwitchUserRepository.findAll(specification, pageable); + } + + @Override + public Page queryByUserEntity(FreeSwitchUserRequest request) { + + UserEntity user = authService.getCurrentUser(); + request.setOrgUid(user.getOrgUid()); + + return queryByOrgEntity(request); + } + + @Override + public Optional findByUid(String uid) { + return freeSwitchUserRepository.findByUidAndDeleted(uid, false); + } + + @Override + public FreeSwitchUserResponse convertToResponse(FreeSwitchUserEntity entity) { + return modelMapper.map(entity, FreeSwitchUserResponse.class); + } + + @Override + public FreeSwitchUserEntity convertToEntity(FreeSwitchUserRequest request) { + return modelMapper.map(request, FreeSwitchUserEntity.class); + } + + @Override + public FreeSwitchUserExcel convertToExcel(FreeSwitchUserEntity entity) { + return modelMapper.map(entity, FreeSwitchUserExcel.class); + } + + @Override + public FreeSwitchUserResponse create(FreeSwitchUserRequest request) { + + UserEntity user = authService.getCurrentUser(); + if (StringUtils.hasText(request.getOrgUid())) { + request.setOrgUid(user.getOrgUid()); + } + + if (StringUtils.hasText(request.getUid())) { + request.setUid(uidUtils.getCacheSerialUid()); + } + + // 检查用户名是否已存在 + if (freeSwitchUserRepository.existsByUsernameAndDomain(request.getUsername(), request.getDomain())) { + throw new RuntimeException("用户名已存在: " + request.getUsername() + "@" + request.getDomain()); + } + + FreeSwitchUserEntity entity = convertToEntity(request); + entity.setLevel(LevelEnum.PLATFORM.name()); + entity.setPlatform(BytedeskConsts.PLATFORM_BYTEDESK); + + FreeSwitchUserEntity savedEntity = save(entity); + if (savedEntity == null) { + throw new RuntimeException("创建FreeSwitch用户失败"); + } + + return convertToResponse(savedEntity); + } + + @Override + public FreeSwitchUserResponse update(FreeSwitchUserRequest request) { + + Optional optional = findByUid(request.getUid()); + if (!optional.isPresent()) { + throw new RuntimeException("FreeSwitch用户不存在"); + } + + FreeSwitchUserEntity entity = optional.get(); + + // 更新字段 + if (StringUtils.hasText(request.getDisplayName())) { + entity.setDisplayName(request.getDisplayName()); + } + if (StringUtils.hasText(request.getEmail())) { + entity.setEmail(request.getEmail()); + } + if (StringUtils.hasText(request.getAccountcode())) { + entity.setAccountcode(request.getAccountcode()); + } + if (request.getEnabled() != null) { + entity.setEnabled(request.getEnabled()); + } + if (StringUtils.hasText(request.getRemarks())) { + entity.setRemarks(request.getRemarks()); + } + + FreeSwitchUserEntity updatedEntity = save(entity); + if (updatedEntity == null) { + throw new RuntimeException("更新FreeSwitch用户失败"); + } + + return convertToResponse(updatedEntity); + } + + @Override + public FreeSwitchUserEntity save(FreeSwitchUserEntity entity) { + try { + return freeSwitchUserRepository.save(entity); + } catch (ObjectOptimisticLockingFailureException e) { + handleOptimisticLockingFailureException(e, entity); + } + return null; + } + + @Override + public void deleteByUid(String uid) { + Optional optional = findByUid(uid); + optional.ifPresent(entity -> { + entity.setDeleted(true); + save(entity); + }); + } + + @Override + @Cacheable(value = "freeswitch_user", key = "#uid", unless = "#result == null") + public FreeSwitchUserResponse queryByUid(FreeSwitchUserRequest request) { + Optional optional = findByUid(request.getUid()); + if (optional.isPresent()) { + return convertToResponse(optional.get()); + } + throw new RuntimeException("FreeSwitch用户不存在"); + } + +} diff --git a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/user/FreeSwitchUserSpecification.java b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/user/FreeSwitchUserSpecification.java index e69de29bb2..4dcefe5c36 100644 --- a/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/user/FreeSwitchUserSpecification.java +++ b/plugins/freeswitch/src/main/java/com/bytedesk/freeswitch/user/FreeSwitchUserSpecification.java @@ -0,0 +1,70 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2025-06-08 10:00:00 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-06-08 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.user; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.data.jpa.domain.Specification; +import org.springframework.util.StringUtils; + +import com.bytedesk.core.base.BaseSpecification; + +import jakarta.persistence.criteria.Predicate; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class FreeSwitchUserSpecification extends BaseSpecification { + + public static Specification search(FreeSwitchUserRequest request) { + log.info("request: {}", request); + return (root, query, criteriaBuilder) -> { + List predicates = new ArrayList<>(); + predicates.addAll(getBasicPredicates(root, criteriaBuilder, request.getOrgUid())); + + // username + if (StringUtils.hasText(request.getUsername())) { + predicates.add(criteriaBuilder.like(root.get("username"), "%" + request.getUsername() + "%")); + } + + // domain + if (StringUtils.hasText(request.getDomain())) { + predicates.add(criteriaBuilder.equal(root.get("domain"), request.getDomain())); + } + + // displayName + if (StringUtils.hasText(request.getDisplayName())) { + predicates.add(criteriaBuilder.like(root.get("displayName"), "%" + request.getDisplayName() + "%")); + } + + // email + if (StringUtils.hasText(request.getEmail())) { + predicates.add(criteriaBuilder.like(root.get("email"), "%" + request.getEmail() + "%")); + } + + // accountcode + if (StringUtils.hasText(request.getAccountcode())) { + predicates.add(criteriaBuilder.equal(root.get("accountcode"), request.getAccountcode())); + } + + // enabled + if (request.getEnabled() != null) { + predicates.add(criteriaBuilder.equal(root.get("enabled"), request.getEnabled())); + } + + return criteriaBuilder.and(predicates.toArray(new Predicate[0])); + }; + } + +}