This commit is contained in:
jack ning
2025-06-08 18:14:22 +08:00
parent c162e85eac
commit 6fa058f340
17 changed files with 1162 additions and 13 deletions

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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<FreeSwitchCdrEntity> search(FreeSwitchCdrRequest request) {
log.info("request: {}", request);
return (root, query, criteriaBuilder) -> {
List<Predicate> 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]));
};
}
}

View File

@@ -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;
}

View File

@@ -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<FreeSwitchConferenceRequest> {
private final FreeSwitchConferenceRestService freeSwitchConferenceRestService;
@ActionAnnotation(title = "FreeSwitch会议室", action = "组织查询", description = "query freeswitch conference by org")
@Override
public ResponseEntity<?> queryByOrg(FreeSwitchConferenceRequest request) {
Page<FreeSwitchConferenceResponse> 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<FreeSwitchConferenceResponse> 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("导出成功"));
}
}

View File

@@ -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<FreeSwitchConferenceEntity, FreeSwitchConferenceRequest, FreeSwitchConferenceResponse, FreeSwitchConferenceExcel> {
private final FreeSwitchConferenceRepository freeSwitchConferenceRepository;
private final ModelMapper modelMapper;
private final UidUtils uidUtils;
private final AuthService authService;
@Override
public Page<FreeSwitchConferenceEntity> queryByOrgEntity(FreeSwitchConferenceRequest request) {
Pageable pageable = Utils.getPageable(request);
Specification<FreeSwitchConferenceEntity> specification = FreeSwitchConferenceSpecification.search(request);
return freeSwitchConferenceRepository.findAll(specification, pageable);
}
@Override
public Page<FreeSwitchConferenceEntity> queryByUserEntity(FreeSwitchConferenceRequest request) {
UserEntity user = authService.getCurrentUser();
request.setOrgUid(user.getOrgUid());
return queryByOrgEntity(request);
}
@Override
public Optional<FreeSwitchConferenceEntity> 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<FreeSwitchConferenceEntity> 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<FreeSwitchConferenceEntity> 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<FreeSwitchConferenceEntity> optional = findByUid(request.getUid());
if (optional.isPresent()) {
return convertToResponse(optional.get());
}
throw new RuntimeException("FreeSwitch会议室不存在");
}
}

View File

@@ -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 {
/**
* 网关名称

View File

@@ -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.

View File

@@ -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;
}

View File

@@ -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<FreeSwitchUserRequest> {
private final FreeSwitchUserRestService freeSwitchUserRestService;
@ActionAnnotation(title = "FreeSwitch用户", action = "组织查询", description = "query freeswitch user by org")
@Override
public ResponseEntity<?> queryByOrg(FreeSwitchUserRequest request) {
Page<FreeSwitchUserResponse> 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<FreeSwitchUserResponse> 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("导出成功"));
}
}

View File

@@ -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<FreeSwitchUserEntity, FreeSwitchUserRequest, FreeSwitchUserResponse, FreeSwitchUserExcel> {
private final FreeSwitchUserRepository freeSwitchUserRepository;
private final ModelMapper modelMapper;
private final UidUtils uidUtils;
private final AuthService authService;
@Override
public Page<FreeSwitchUserEntity> queryByOrgEntity(FreeSwitchUserRequest request) {
Pageable pageable = Utils.getPageable(request);
Specification<FreeSwitchUserEntity> specification = FreeSwitchUserSpecification.search(request);
return freeSwitchUserRepository.findAll(specification, pageable);
}
@Override
public Page<FreeSwitchUserEntity> queryByUserEntity(FreeSwitchUserRequest request) {
UserEntity user = authService.getCurrentUser();
request.setOrgUid(user.getOrgUid());
return queryByOrgEntity(request);
}
@Override
public Optional<FreeSwitchUserEntity> 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<FreeSwitchUserEntity> 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<FreeSwitchUserEntity> 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<FreeSwitchUserEntity> optional = findByUid(request.getUid());
if (optional.isPresent()) {
return convertToResponse(optional.get());
}
throw new RuntimeException("FreeSwitch用户不存在");
}
}

View File

@@ -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<FreeSwitchUserEntity> search(FreeSwitchUserRequest request) {
log.info("request: {}", request);
return (root, query, criteriaBuilder) -> {
List<Predicate> 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]));
};
}
}