From e94100ccef608aeaaeb28c3da9ee8742e77d39d0 Mon Sep 17 00:00:00 2001 From: jack ning Date: Thu, 24 Jul 2025 20:50:42 +0800 Subject: [PATCH] update --- .../com/bytedesk/core/host/HostEntity.java | 79 ----- .../bytedesk/core/host/HostEventListener.java | 44 --- .../com/bytedesk/core/host/HostInitData.java | 122 ------- .../bytedesk/core/host/HostRepository.java | 30 -- .../com/bytedesk/core/host/HostRequest.java | 42 --- .../com/bytedesk/core/host/HostResponse.java | 44 --- .../core/host/HostRestController.java | 124 ------- .../bytedesk/core/host/HostRestService.java | 221 ------------ .../bytedesk/core/server/ServerEntity.java | 185 +++++++++++ .../ServerEntityListener.java} | 24 +- .../ServerEventListener.java} | 28 +- .../ServerExcel.java} | 26 +- .../ServerInitializer.java} | 15 +- .../ServerPermissions.java} | 4 +- .../core/server/ServerRepository.java | 128 +++++++ .../bytedesk/core/server/ServerRequest.java | 184 ++++++++++ .../bytedesk/core/server/ServerResponse.java | 303 +++++++++++++++++ .../core/server/ServerRestController.java | 124 +++++++ .../core/server/ServerRestService.java | 232 +++++++++++++ .../bytedesk/core/server/ServerService.java | 314 ++++++++++++++++++ .../ServerSpecification.java} | 25 +- .../core/server/ServerStatusEnum.java | 95 ++++++ .../bytedesk/core/server/ServerTypeEnum.java | 97 ++++++ .../event/ServerCreateEvent.java} | 14 +- .../event/ServerDeleteEvent.java} | 14 +- .../event/ServerUpdateEvent.java} | 14 +- .../core/{host => server}/package-info.java | 2 +- 27 files changed, 1731 insertions(+), 803 deletions(-) delete mode 100644 modules/core/src/main/java/com/bytedesk/core/host/HostEntity.java delete mode 100644 modules/core/src/main/java/com/bytedesk/core/host/HostEventListener.java delete mode 100644 modules/core/src/main/java/com/bytedesk/core/host/HostInitData.java delete mode 100644 modules/core/src/main/java/com/bytedesk/core/host/HostRepository.java delete mode 100644 modules/core/src/main/java/com/bytedesk/core/host/HostRequest.java delete mode 100644 modules/core/src/main/java/com/bytedesk/core/host/HostResponse.java delete mode 100644 modules/core/src/main/java/com/bytedesk/core/host/HostRestController.java delete mode 100644 modules/core/src/main/java/com/bytedesk/core/host/HostRestService.java create mode 100644 modules/core/src/main/java/com/bytedesk/core/server/ServerEntity.java rename modules/core/src/main/java/com/bytedesk/core/{host/HostEntityListener.java => server/ServerEntityListener.java} (65%) rename modules/core/src/main/java/com/bytedesk/core/{host/HostTypeEnum.java => server/ServerEventListener.java} (56%) rename modules/core/src/main/java/com/bytedesk/core/{host/HostExcel.java => server/ServerExcel.java} (57%) rename modules/core/src/main/java/com/bytedesk/core/{host/HostInitializer.java => server/ServerInitializer.java} (69%) rename modules/core/src/main/java/com/bytedesk/core/{host/HostPermissions.java => server/ServerPermissions.java} (88%) create mode 100644 modules/core/src/main/java/com/bytedesk/core/server/ServerRepository.java create mode 100644 modules/core/src/main/java/com/bytedesk/core/server/ServerRequest.java create mode 100644 modules/core/src/main/java/com/bytedesk/core/server/ServerResponse.java create mode 100644 modules/core/src/main/java/com/bytedesk/core/server/ServerRestController.java create mode 100644 modules/core/src/main/java/com/bytedesk/core/server/ServerRestService.java create mode 100644 modules/core/src/main/java/com/bytedesk/core/server/ServerService.java rename modules/core/src/main/java/com/bytedesk/core/{host/HostSpecification.java => server/ServerSpecification.java} (56%) create mode 100644 modules/core/src/main/java/com/bytedesk/core/server/ServerStatusEnum.java create mode 100644 modules/core/src/main/java/com/bytedesk/core/server/ServerTypeEnum.java rename modules/core/src/main/java/com/bytedesk/core/{host/event/HostCreateEvent.java => server/event/ServerCreateEvent.java} (75%) rename modules/core/src/main/java/com/bytedesk/core/{host/event/HostDeleteEvent.java => server/event/ServerDeleteEvent.java} (75%) rename modules/core/src/main/java/com/bytedesk/core/{host/event/HostUpdateEvent.java => server/event/ServerUpdateEvent.java} (75%) rename modules/core/src/main/java/com/bytedesk/core/{host => server}/package-info.java (63%) diff --git a/modules/core/src/main/java/com/bytedesk/core/host/HostEntity.java b/modules/core/src/main/java/com/bytedesk/core/host/HostEntity.java deleted file mode 100644 index 10f5f19dd3..0000000000 --- a/modules/core/src/main/java/com/bytedesk/core/host/HostEntity.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * @Author: jackning 270580156@qq.com - * @Date: 2024-05-11 18:14:28 - * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2025-07-24 19:56:39 - * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * contact: 270580156@qq.com - * 联系:270580156@qq.com - * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. - */ -package com.bytedesk.core.host; - -import com.bytedesk.core.base.BaseEntity; -import com.bytedesk.core.constant.I18Consts; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -// import jakarta.persistence.EntityListeners; -import jakarta.persistence.Table; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import lombok.experimental.Accessors; -import lombok.experimental.SuperBuilder; - -/** - * Host entity for content categorization and organization - * Provides host functionality for various system entities - * - * Database Table: bytedesk_core_host - * Purpose: Stores host definitions, colors, and organization settings - */ -@Entity -@Data -@SuperBuilder -@Accessors(chain = true) -@EqualsAndHashCode(callSuper = true) -@AllArgsConstructor -@NoArgsConstructor -// @EntityListeners({HostEntityListener.class}) -@Table(name = "bytedesk_core_host") -public class HostEntity extends BaseEntity { - - /** - * Name of the host - */ - private String name; - - /** - * Description of the host - */ - @Builder.Default - private String description = I18Consts.I18N_DESCRIPTION; - - /** - * Type of host (CUSTOMER, TICKET, ARTICLE, etc.) - */ - @Builder.Default - @Column(name = "host_type") - private String type = HostTypeEnum.CUSTOMER.name(); - - /** - * Color theme for the host display - */ - @Builder.Default - @Column(name = "host_color") - private String color = "red"; - - /** - * Display order of the host - */ - @Builder.Default - @Column(name = "host_order") - private Integer order = 0; -} diff --git a/modules/core/src/main/java/com/bytedesk/core/host/HostEventListener.java b/modules/core/src/main/java/com/bytedesk/core/host/HostEventListener.java deleted file mode 100644 index bdbb850377..0000000000 --- a/modules/core/src/main/java/com/bytedesk/core/host/HostEventListener.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * @Author: jackning 270580156@qq.com - * @Date: 2025-02-25 09:44:18 - * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2025-06-04 15:50:06 - * @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.core.host; - -import org.springframework.context.event.EventListener; -import org.springframework.core.annotation.Order; -import org.springframework.stereotype.Component; - -import com.bytedesk.core.rbac.organization.OrganizationEntity; -import com.bytedesk.core.rbac.organization.event.OrganizationCreateEvent; - -import lombok.AllArgsConstructor; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -@Component -@AllArgsConstructor -public class HostEventListener { - - private final HostRestService hostRestService; - - @Order(3) - @EventListener - public void onOrganizationCreateEvent(OrganizationCreateEvent event) { - OrganizationEntity organization = (OrganizationEntity) event.getSource(); - String orgUid = organization.getUid(); - log.info("thread - organization created: {}", organization.getName()); - hostRestService.initHosts(orgUid); - } - - -} - diff --git a/modules/core/src/main/java/com/bytedesk/core/host/HostInitData.java b/modules/core/src/main/java/com/bytedesk/core/host/HostInitData.java deleted file mode 100644 index 86bbd34e42..0000000000 --- a/modules/core/src/main/java/com/bytedesk/core/host/HostInitData.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * @Author: jackning 270580156@qq.com - * @Date: 2025-03-11 08:54:35 - * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2025-06-04 17:12:37 - * @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.core.host; - -// import com.bytedesk.core.constant.I18Consts; - -public class HostInitData { - - /** - * Technical Support Hosts - * 技术支持标签 - */ - public static final String[] TECHNICAL_SUPPORT = { - // I18Consts.I18N_PREFIX + "thread.host.technical_support", // parent - "技术支持", // parent - }; - - /** - * Service Request Hosts - * 服务请求标签 - */ - public static final String[] SERVICE_REQUEST = { - // I18Consts.I18N_PREFIX + "thread.host.service_request", // parent - "服务请求", // parent - }; - - /** - * Consultation Hosts - * 咨询标签 - */ - public static final String[] CONSULTATION = { - // I18Consts.I18N_PREFIX + "thread.host.consultation", // parent - "咨询", // parent - }; - - /** - * Complaint & Suggestion Hosts - * 投诉与建议标签 - */ - public static final String[] COMPLAINT_SUGGESTION = { - // I18Consts.I18N_PREFIX + "thread.host.complaint_suggestion", // parent - "投诉建议", // parent - }; - - /** - * Operation & Maintenance Hosts - * 运维标签 - */ - public static final String[] OPERATION_MAINTENANCE = { - // I18Consts.I18N_PREFIX + "thread.host.operation_maintenance", // parent - "运维", // parent - // 其他 - // I18Consts.I18N_PREFIX + "thread.host.other", - "其他", - }; - - /** - * Helper method to determine if a host is a parent host - * - * @param host The host key to check - * @return true if it's a parent host - */ - public static boolean isParentHost(String host) { - return !host.contains("."); - } - - /** - * Helper method to get parent host key for a child host - * - * @param childHost The child host key - * @return The parent host key - */ - public static String getParentHost(String childHost) { - if (isParentHost(childHost)) { - return null; - } - // 由于已将常量转为中文,此方法可能需要重新实现 - // 这里仅保留基本结构,具体实现需要根据新的标签体系来调整 - return null; - } - - /** - * Get all hosts as a single array - * - * @return Array containing all hosts - */ - public static String[] getAllHosts() { - int totalLength = TECHNICAL_SUPPORT.length + SERVICE_REQUEST.length + - CONSULTATION.length + COMPLAINT_SUGGESTION.length + - OPERATION_MAINTENANCE.length; - - String[] allHosts = new String[totalLength]; - int index = 0; - - System.arraycopy(TECHNICAL_SUPPORT, 0, allHosts, index, TECHNICAL_SUPPORT.length); - index += TECHNICAL_SUPPORT.length; - - System.arraycopy(SERVICE_REQUEST, 0, allHosts, index, SERVICE_REQUEST.length); - index += SERVICE_REQUEST.length; - - System.arraycopy(CONSULTATION, 0, allHosts, index, CONSULTATION.length); - index += CONSULTATION.length; - - System.arraycopy(COMPLAINT_SUGGESTION, 0, allHosts, index, COMPLAINT_SUGGESTION.length); - index += COMPLAINT_SUGGESTION.length; - - System.arraycopy(OPERATION_MAINTENANCE, 0, allHosts, index, OPERATION_MAINTENANCE.length); - - return allHosts; - } -} \ No newline at end of file diff --git a/modules/core/src/main/java/com/bytedesk/core/host/HostRepository.java b/modules/core/src/main/java/com/bytedesk/core/host/HostRepository.java deleted file mode 100644 index 567f93d2d2..0000000000 --- a/modules/core/src/main/java/com/bytedesk/core/host/HostRepository.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * @Author: jackning 270580156@qq.com - * @Date: 2024-05-11 18:25:55 - * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2025-06-20 12:52:47 - * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * contact: 270580156@qq.com - * 联系:270580156@qq.com - * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. - */ -package com.bytedesk.core.host; - -import java.util.Optional; - -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.JpaSpecificationExecutor; - -public interface HostRepository extends JpaRepository, JpaSpecificationExecutor { - - Optional findByUid(String uid); - - Boolean existsByUid(String uid); - - Optional findByNameAndOrgUidAndTypeAndDeletedFalse(String name, String orgUid, String type); - - // Boolean existsByPlatform(String platform); -} diff --git a/modules/core/src/main/java/com/bytedesk/core/host/HostRequest.java b/modules/core/src/main/java/com/bytedesk/core/host/HostRequest.java deleted file mode 100644 index 55025dfbd2..0000000000 --- a/modules/core/src/main/java/com/bytedesk/core/host/HostRequest.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * @Author: jackning 270580156@qq.com - * @Date: 2024-05-11 18:26:04 - * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2025-06-20 14:24: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. - * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * contact: 270580156@qq.com - * 联系:270580156@qq.com - * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. - */ -package com.bytedesk.core.host; - -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; - -@Data -@SuperBuilder -@Accessors(chain = true) -@EqualsAndHashCode(callSuper = false) -@AllArgsConstructor -@NoArgsConstructor -public class HostRequest extends BaseRequest { - - private String name; - - private String description; - - // @Builder.Default - // private String type = HostTypeEnum.CUSTOMER.name(); - - private String color; - - private Integer order; -} diff --git a/modules/core/src/main/java/com/bytedesk/core/host/HostResponse.java b/modules/core/src/main/java/com/bytedesk/core/host/HostResponse.java deleted file mode 100644 index f812bb0761..0000000000 --- a/modules/core/src/main/java/com/bytedesk/core/host/HostResponse.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * @Author: jackning 270580156@qq.com - * @Date: 2024-05-11 18:26:12 - * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2025-06-04 15:36:28 - * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * contact: 270580156@qq.com - * 联系:270580156@qq.com - * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. - */ -package com.bytedesk.core.host; - - -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; - -@Data -@SuperBuilder -@Accessors(chain = true) -@EqualsAndHashCode(callSuper = true) -@AllArgsConstructor -@NoArgsConstructor -public class HostResponse extends BaseResponse { - - private String name; - - private String description; - - private String type; - - private String color; - - private Integer order; - -} diff --git a/modules/core/src/main/java/com/bytedesk/core/host/HostRestController.java b/modules/core/src/main/java/com/bytedesk/core/host/HostRestController.java deleted file mode 100644 index 9966bac8a4..0000000000 --- a/modules/core/src/main/java/com/bytedesk/core/host/HostRestController.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * @Author: jackning 270580156@qq.com - * @Date: 2024-05-11 18:25:36 - * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2025-07-24 19:55:50 - * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * contact: 270580156@qq.com - * 联系:270580156@qq.com - * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. - */ -package com.bytedesk.core.host; - -import org.springframework.data.domain.Page; -import org.springframework.http.ResponseEntity; -// import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.context.annotation.Description; - -import com.bytedesk.core.annotation.ActionAnnotation; -import com.bytedesk.core.base.BaseRestController; -import com.bytedesk.core.utils.JsonResult; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.servlet.http.HttpServletResponse; -import lombok.AllArgsConstructor; - -@RestController -@RequestMapping("/api/v1/host") -@AllArgsConstructor -@Tag(name = "Host Management", description = "Host management APIs for organizing and categorizing content with hosts") -@Description("Host Management Controller - Content host and categorization APIs") -public class HostRestController extends BaseRestController { - - private final HostRestService hostRestService; - - // @PreAuthorize(RolePermissions.ROLE_ADMIN) - @ActionAnnotation(title = "Host", action = "org query", description = "query host by org") - @Operation(summary = "Query Hosts by Organization", description = "Retrieve hosts for the current organization") - @Override - public ResponseEntity queryByOrg(HostRequest request) { - - Page hosts = hostRestService.queryByOrg(request); - - return ResponseEntity.ok(JsonResult.success(hosts)); - } - - @ActionAnnotation(title = "Host", action = "user query", description = "query host by user") - @Operation(summary = "Query Hosts by User", description = "Retrieve hosts for the current user") - @Override - public ResponseEntity queryByUser(HostRequest request) { - - Page hosts = hostRestService.queryByUser(request); - - return ResponseEntity.ok(JsonResult.success(hosts)); - } - - @ActionAnnotation(title = "Host", action = "query detail", description = "query host by uid") - @Operation(summary = "Query Host by UID", description = "Retrieve a specific host by its unique identifier") - @Override - public ResponseEntity queryByUid(HostRequest request) { - - HostResponse host = hostRestService.queryByUid(request); - - return ResponseEntity.ok(JsonResult.success(host)); - } - - @ActionAnnotation(title = "Host", action = "create", description = "create host") - @Operation(summary = "Create Host", description = "Create a new host") - @Override - // @PreAuthorize("hasAuthority('TAG_CREATE')") - public ResponseEntity create(HostRequest request) { - - HostResponse host = hostRestService.create(request); - - return ResponseEntity.ok(JsonResult.success(host)); - } - - @ActionAnnotation(title = "Host", action = "update", description = "update host") - @Operation(summary = "Update Host", description = "Update an existing host") - @Override - // @PreAuthorize("hasAuthority('TAG_UPDATE')") - public ResponseEntity update(HostRequest request) { - - HostResponse host = hostRestService.update(request); - - return ResponseEntity.ok(JsonResult.success(host)); - } - - @ActionAnnotation(title = "Host", action = "delete", description = "delete host") - @Operation(summary = "Delete Host", description = "Delete a host") - @Override - // @PreAuthorize("hasAuthority('TAG_DELETE')") - public ResponseEntity delete(HostRequest request) { - - hostRestService.delete(request); - - return ResponseEntity.ok(JsonResult.success()); - } - - @ActionAnnotation(title = "Host", action = "export", description = "export host") - @Operation(summary = "Export Hosts", description = "Export hosts to Excel format") - @Override - // @PreAuthorize("hasAuthority('TAG_EXPORT')") - @GetMapping("/export") - public Object export(HostRequest request, HttpServletResponse response) { - return exportTemplate( - request, - response, - hostRestService, - HostExcel.class, - "Host", - "host" - ); - } - - - -} \ No newline at end of file diff --git a/modules/core/src/main/java/com/bytedesk/core/host/HostRestService.java b/modules/core/src/main/java/com/bytedesk/core/host/HostRestService.java deleted file mode 100644 index c35719dcf4..0000000000 --- a/modules/core/src/main/java/com/bytedesk/core/host/HostRestService.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * @Author: jackning 270580156@qq.com - * @Date: 2024-05-11 18:25:45 - * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2025-07-02 11:05:26 - * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk - * Please be aware of the BSL license restrictions before installing Bytedesk IM – - * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. - * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE - * contact: 270580156@qq.com - * 联系:270580156@qq.com - * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. - */ -package com.bytedesk.core.host; - -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.transaction.annotation.Transactional; -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 HostRestService extends BaseRestServiceWithExcel { - - private final HostRepository hostRepository; - - private final ModelMapper modelMapper; - - private final UidUtils uidUtils; - - private final AuthService authService; - - @Override - public Page queryByOrgEntity(HostRequest request) { - Pageable pageable = request.getPageable(); - Specification spec = HostSpecification.search(request); - return hostRepository.findAll(spec, pageable); - } - - @Override - public Page queryByOrg(HostRequest request) { - Page page = queryByOrgEntity(request); - return page.map(this::convertToResponse); - } - - @Override - public Page queryByUser(HostRequest request) { - UserEntity user = authService.getUser(); - if (user == null) { - throw new RuntimeException("login first"); - } - request.setUserUid(user.getUid()); - // - return queryByOrg(request); - } - - @Override - public HostResponse queryByUid(HostRequest request) { - Optional optional = findByUid(request.getUid()); - if (optional.isPresent()) { - HostEntity entity = optional.get(); - return convertToResponse(entity); - } else { - throw new RuntimeException("Host not found"); - } - } - - @Cacheable(value = "host", key = "#uid", unless="#result==null") - @Override - public Optional findByUid(String uid) { - return hostRepository.findByUid(uid); - } - - @Cacheable(value = "host", key = "#name + '_' + #orgUid + '_' + #type", unless="#result==null") - public Optional findByNameAndOrgUidAndType(String name, String orgUid, String type) { - return hostRepository.findByNameAndOrgUidAndTypeAndDeletedFalse(name, orgUid, type); - } - - public Boolean existsByUid(String uid) { - return hostRepository.existsByUid(uid); - } - - @Transactional - @Override - public HostResponse create(HostRequest request) { - // 判断是否已经存在 - if (StringUtils.hasText(request.getUid()) && existsByUid(request.getUid())) { - return convertToResponse(findByUid(request.getUid()).get()); - } - // 检查name+orgUid+type是否已经存在 - if (StringUtils.hasText(request.getName()) && StringUtils.hasText(request.getOrgUid()) && StringUtils.hasText(request.getType())) { - Optional host = findByNameAndOrgUidAndType(request.getName(), request.getOrgUid(), request.getType()); - if (host.isPresent()) { - return convertToResponse(host.get()); - } - } - // - UserEntity user = authService.getUser(); - if (user != null) { - request.setUserUid(user.getUid()); - } - // - HostEntity entity = modelMapper.map(request, HostEntity.class); - if (!StringUtils.hasText(request.getUid())) { - entity.setUid(uidUtils.getUid()); - } - // - HostEntity savedEntity = save(entity); - if (savedEntity == null) { - throw new RuntimeException("Create host failed"); - } - return convertToResponse(savedEntity); - } - - @Transactional - @Override - public HostResponse update(HostRequest request) { - Optional optional = hostRepository.findByUid(request.getUid()); - if (optional.isPresent()) { - HostEntity entity = optional.get(); - modelMapper.map(request, entity); - // - HostEntity savedEntity = save(entity); - if (savedEntity == null) { - throw new RuntimeException("Update host failed"); - } - return convertToResponse(savedEntity); - } - else { - throw new RuntimeException("Host not found"); - } - } - - @Override - protected HostEntity doSave(HostEntity entity) { - return hostRepository.save(entity); - } - - @Override - public HostEntity handleOptimisticLockingFailureException(ObjectOptimisticLockingFailureException e, HostEntity entity) { - try { - Optional latest = hostRepository.findByUid(entity.getUid()); - if (latest.isPresent()) { - HostEntity latestEntity = latest.get(); - // 合并需要保留的数据 - latestEntity.setName(entity.getName()); - // latestEntity.setOrder(entity.getOrder()); - // latestEntity.setDeleted(entity.isDeleted()); - return hostRepository.save(latestEntity); - } - } catch (Exception ex) { - log.error("无法处理乐观锁冲突: {}", ex.getMessage(), ex); - throw new RuntimeException("无法处理乐观锁冲突: " + ex.getMessage(), ex); - } - return null; - } - - @Transactional - @Override - public void deleteByUid(String uid) { - Optional optional = hostRepository.findByUid(uid); - if (optional.isPresent()) { - optional.get().setDeleted(true); - save(optional.get()); - // hostRepository.delete(optional.get()); - } - else { - throw new RuntimeException("Host not found"); - } - } - - @Override - public void delete(HostRequest request) { - deleteByUid(request.getUid()); - } - - @Override - public HostResponse convertToResponse(HostEntity entity) { - return modelMapper.map(entity, HostResponse.class); - } - - @Override - public HostExcel convertToExcel(HostEntity entity) { - return modelMapper.map(entity, HostExcel.class); - } - - public void initHosts(String orgUid) { - // log.info("initThreadHost"); - for (String host : HostInitData.getAllHosts()) { - HostRequest hostRequest = HostRequest.builder() - .uid(Utils.formatUid(orgUid, host)) - .name(host) - .order(0) - .type(HostTypeEnum.THREAD.name()) - .level(LevelEnum.ORGANIZATION.name()) - .platform(BytedeskConsts.PLATFORM_BYTEDESK) - .orgUid(orgUid) - .build(); - create(hostRequest); - } - } - -} diff --git a/modules/core/src/main/java/com/bytedesk/core/server/ServerEntity.java b/modules/core/src/main/java/com/bytedesk/core/server/ServerEntity.java new file mode 100644 index 0000000000..8a32c2addd --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/server/ServerEntity.java @@ -0,0 +1,185 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-05-11 18:14:28 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-07-24 20:36:13 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.server; + +import com.bytedesk.core.base.BaseEntity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import lombok.experimental.SuperBuilder; + +import java.time.LocalDateTime; + +/** + * Server entity for monitoring server resources and status + * Provides server monitoring functionality for system administration + * + * Database Table: bytedesk_core_server + * Purpose: Stores server information, resource usage, and monitoring data + */ +@Entity +@Data +@SuperBuilder +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +@AllArgsConstructor +@NoArgsConstructor +@Table(name = "bytedesk_core_server") +public class ServerEntity extends BaseEntity { + + /** + * Server hostname or identifier + */ + @Column(nullable = false) + private String serverName; + + /** + * Server IP address + */ + private String serverIp; + + /** + * Server type (APPLICATION, DATABASE, CACHE, etc.) + */ + @Builder.Default + @Column(name = "server_type") + private String type = ServerTypeEnum.APPLICATION.name(); + + /** + * Server status (ONLINE, OFFLINE, MAINTENANCE, etc.) + */ + @Builder.Default + @Column(name = "server_status") + private String status = ServerStatusEnum.ONLINE.name(); + + /** + * Server description + */ + @Column(name = "server_description") + private String description; + + /** + * CPU usage percentage (0-100) + */ + @Builder.Default + private Double cpuUsage = 0.0; + + /** + * Memory usage percentage (0-100) + */ + @Builder.Default + private Double memoryUsage = 0.0; + + /** + * Total memory in MB + */ + private Long totalMemoryMb; + + /** + * Used memory in MB + */ + private Long usedMemoryMb; + + /** + * Disk usage percentage (0-100) + */ + @Builder.Default + private Double diskUsage = 0.0; + + /** + * Total disk space in GB + */ + private Long totalDiskGb; + + /** + * Used disk space in GB + */ + private Long usedDiskGb; + + /** + * Server uptime in seconds + */ + private Long uptimeSeconds; + + /** + * Server start time + */ + private LocalDateTime startTime; + + /** + * Last heartbeat time + */ + private LocalDateTime lastHeartbeat; + + /** + * Server port (if applicable) + */ + private Integer serverPort; + + /** + * Operating system information + */ + private String osInfo; + + /** + * Java version (if applicable) + */ + private String javaVersion; + + /** + * Application version + */ + private String appVersion; + + /** + * Environment (DEV, TEST, PROD, etc.) + */ + @Builder.Default + private String environment = "DEV"; + + /** + * Server location or data center + */ + private String location; + + /** + * Monitoring enabled flag + */ + @Builder.Default + private Boolean monitoringEnabled = true; + + /** + * Alert threshold for CPU usage + */ + @Builder.Default + private Double cpuAlertThreshold = 80.0; + + /** + * Alert threshold for memory usage + */ + @Builder.Default + private Double memoryAlertThreshold = 80.0; + + /** + * Alert threshold for disk usage + */ + @Builder.Default + private Double diskAlertThreshold = 85.0; +} \ No newline at end of file diff --git a/modules/core/src/main/java/com/bytedesk/core/host/HostEntityListener.java b/modules/core/src/main/java/com/bytedesk/core/server/ServerEntityListener.java similarity index 65% rename from modules/core/src/main/java/com/bytedesk/core/host/HostEntityListener.java rename to modules/core/src/main/java/com/bytedesk/core/server/ServerEntityListener.java index d70c5c9a59..13b7917960 100644 --- a/modules/core/src/main/java/com/bytedesk/core/host/HostEntityListener.java +++ b/modules/core/src/main/java/com/bytedesk/core/server/ServerEntityListener.java @@ -11,14 +11,14 @@ * * Copyright (c) 2025 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.core.host; +package com.bytedesk.core.server; import org.springframework.stereotype.Component; import org.springframework.util.SerializationUtils; import com.bytedesk.core.config.BytedeskEventPublisher; -import com.bytedesk.core.host.event.HostCreateEvent; -import com.bytedesk.core.host.event.HostUpdateEvent; +import com.bytedesk.core.server.event.ServerCreateEvent; +import com.bytedesk.core.server.event.ServerUpdateEvent; import com.bytedesk.core.utils.ApplicationContextHolder; import jakarta.persistence.PostPersist; @@ -28,24 +28,24 @@ import lombok.extern.slf4j.Slf4j; @Slf4j @Component -public class HostEntityListener { +public class ServerEntityListener { @PostPersist - public void onPostPersist(HostEntity host) { - log.info("onPostPersist: {}", host); - HostEntity cloneHost = SerializationUtils.clone(host); + public void onPostPersist(ServerEntity server) { + log.info("onPostPersist: {}", server); + ServerEntity cloneServer = SerializationUtils.clone(server); // BytedeskEventPublisher bytedeskEventPublisher = ApplicationContextHolder.getBean(BytedeskEventPublisher.class); - bytedeskEventPublisher.publishEvent(new HostCreateEvent(cloneHost)); + bytedeskEventPublisher.publishEvent(new ServerCreateEvent(cloneServer)); } @PostUpdate - public void onPostUpdate(HostEntity host) { - log.info("onPostUpdate: {}", host); - HostEntity cloneHost = SerializationUtils.clone(host); + public void onPostUpdate(ServerEntity server) { + log.info("onPostUpdate: {}", server); + ServerEntity cloneServer = SerializationUtils.clone(server); // BytedeskEventPublisher bytedeskEventPublisher = ApplicationContextHolder.getBean(BytedeskEventPublisher.class); - bytedeskEventPublisher.publishEvent(new HostUpdateEvent(cloneHost)); + bytedeskEventPublisher.publishEvent(new ServerUpdateEvent(cloneServer)); } } diff --git a/modules/core/src/main/java/com/bytedesk/core/host/HostTypeEnum.java b/modules/core/src/main/java/com/bytedesk/core/server/ServerEventListener.java similarity index 56% rename from modules/core/src/main/java/com/bytedesk/core/host/HostTypeEnum.java rename to modules/core/src/main/java/com/bytedesk/core/server/ServerEventListener.java index eaed4ccd94..efaec00b45 100644 --- a/modules/core/src/main/java/com/bytedesk/core/host/HostTypeEnum.java +++ b/modules/core/src/main/java/com/bytedesk/core/server/ServerEventListener.java @@ -1,20 +1,28 @@ /* * @Author: jackning 270580156@qq.com - * @Date: 2024-07-23 17:02:46 + * @Date: 2025-02-25 09:44:18 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2025-03-11 08:57:11 + * @LastEditTime: 2025-07-24 20:45:51 * @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. + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE * contact: 270580156@qq.com - * 联系:270580156@qq.com - * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + * + * Copyright (c) 2025 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.core.host; +package com.bytedesk.core.server; -public enum HostTypeEnum { - THREAD, - CUSTOMER, - TICKET +import org.springframework.stereotype.Component; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Component +@AllArgsConstructor +public class ServerEventListener { + + } + diff --git a/modules/core/src/main/java/com/bytedesk/core/host/HostExcel.java b/modules/core/src/main/java/com/bytedesk/core/server/ServerExcel.java similarity index 57% rename from modules/core/src/main/java/com/bytedesk/core/host/HostExcel.java rename to modules/core/src/main/java/com/bytedesk/core/server/ServerExcel.java index 09006cfe8e..633298967b 100644 --- a/modules/core/src/main/java/com/bytedesk/core/host/HostExcel.java +++ b/modules/core/src/main/java/com/bytedesk/core/server/ServerExcel.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-08-01 06:18:10 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2025-07-04 18:00:51 + * @LastEditTime: 2025-07-24 20:37:52 * @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. @@ -11,12 +11,9 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.core.host; - -import java.time.ZonedDateTime; +package com.bytedesk.core.server; import com.alibaba.excel.annotation.ExcelProperty; -import com.alibaba.excel.annotation.format.DateTimeFormat; import com.alibaba.excel.annotation.write.style.ColumnWidth; import lombok.Data; @@ -25,23 +22,14 @@ import lombok.Data; * https://github.com/alibaba/easyexcel */ @Data -public class HostExcel { +public class ServerExcel { - @ExcelProperty(index = 0, value = "标签名称") + @ExcelProperty(index = 0, value = "服务器名称") @ColumnWidth(20) - private String name; + private String serverName; - @ExcelProperty(index = 1, value = "类型") + @ExcelProperty(index = 1, value = "服务器IP") @ColumnWidth(20) - private String type; - - @ExcelProperty(index = 2, value = "颜色") - @ColumnWidth(20) - private String color; - - @DateTimeFormat("yyyy-MM-dd HH:mm:ss") - @ExcelProperty(value = "创建时间", converter = com.bytedesk.core.converter.ZonedDateTimeConverter.class) - @ColumnWidth(25) - private ZonedDateTime createdAt; + private String serverIp; } diff --git a/modules/core/src/main/java/com/bytedesk/core/host/HostInitializer.java b/modules/core/src/main/java/com/bytedesk/core/server/ServerInitializer.java similarity index 69% rename from modules/core/src/main/java/com/bytedesk/core/host/HostInitializer.java rename to modules/core/src/main/java/com/bytedesk/core/server/ServerInitializer.java index cc1473b9e8..13e9c2cadc 100644 --- a/modules/core/src/main/java/com/bytedesk/core/host/HostInitializer.java +++ b/modules/core/src/main/java/com/bytedesk/core/server/ServerInitializer.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-11-06 21:43:58 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2025-06-04 17:09:29 + * @LastEditTime: 2025-07-24 20:36:45 * @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. @@ -11,32 +11,25 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.core.host; +package com.bytedesk.core.server; import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.stereotype.Component; -import com.bytedesk.core.constant.BytedeskConsts; - import lombok.AllArgsConstructor; @Component @AllArgsConstructor -public class HostInitializer implements SmartInitializingSingleton { - - private final HostRestService hostRestService; +public class ServerInitializer implements SmartInitializingSingleton { @Override public void afterSingletonsInstantiated() { initPermissions(); - // 创建默认的工单分类 - String orgUid = BytedeskConsts.DEFAULT_ORGANIZATION_UID; - hostRestService.initHosts(orgUid); } private void initPermissions() { // for (PermissionEnum permission : PermissionEnum.values()) { - // String permissionValue = HostPermissions.ARTICLE_PREFIX + permission.name(); + // String permissionValue = ServerPermissions.ARTICLE_PREFIX + permission.name(); // authorityService.createForPlatform(permissionValue); // } } diff --git a/modules/core/src/main/java/com/bytedesk/core/host/HostPermissions.java b/modules/core/src/main/java/com/bytedesk/core/server/ServerPermissions.java similarity index 88% rename from modules/core/src/main/java/com/bytedesk/core/host/HostPermissions.java rename to modules/core/src/main/java/com/bytedesk/core/server/ServerPermissions.java index 90228ed987..3311b1d913 100644 --- a/modules/core/src/main/java/com/bytedesk/core/host/HostPermissions.java +++ b/modules/core/src/main/java/com/bytedesk/core/server/ServerPermissions.java @@ -11,10 +11,10 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.core.host; +package com.bytedesk.core.server; import com.bytedesk.core.base.BasePermissions; -public class HostPermissions extends BasePermissions { +public class ServerPermissions extends BasePermissions { } diff --git a/modules/core/src/main/java/com/bytedesk/core/server/ServerRepository.java b/modules/core/src/main/java/com/bytedesk/core/server/ServerRepository.java new file mode 100644 index 0000000000..9b332729a9 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/server/ServerRepository.java @@ -0,0 +1,128 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-05-11 18:14:28 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-07-24 19:56:39 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.server; + +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 java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; + +/** + * Repository for ServerEntity + * Provides database operations for server monitoring data + */ +@Repository +public interface ServerRepository extends JpaRepository, JpaSpecificationExecutor { + + /** + * Find server by UID + * @param uid server UID + * @return Optional + */ + Optional findByUid(String uid); + + /** + * Find server by server name and organization UID + * @param serverName server name + * @param orgUid organization UID + * @return Optional + */ + Optional findByServerNameAndOrgUidAndDeletedFalse(String serverName, String orgUid); + + /** + * Find servers by organization UID + * @param orgUid organization UID + * @return List + */ + List findByOrgUidAndDeletedFalseOrderByCreatedAtDesc(String orgUid); + + /** + * Find servers by type + * @param type server type + * @param orgUid organization UID + * @return List + */ + List findByTypeAndOrgUidAndDeletedFalse(String type, String orgUid); + + /** + * Find servers by status + * @param status server status + * @param orgUid organization UID + * @return List + */ + List findByStatusAndOrgUidAndDeletedFalse(String status, String orgUid); + + /** + * Find servers with high resource usage + * @param orgUid organization UID + * @param cpuThreshold CPU usage threshold + * @param memoryThreshold memory usage threshold + * @param diskThreshold disk usage threshold + * @return List + */ + @Query("SELECT s FROM ServerEntity s WHERE s.orgUid = :orgUid AND s.deleted = false " + + "AND (s.cpuUsage >= :cpuThreshold OR s.memoryUsage >= :memoryThreshold OR s.diskUsage >= :diskThreshold)") + List findServersWithHighUsage(@Param("orgUid") String orgUid, + @Param("cpuThreshold") Double cpuThreshold, + @Param("memoryThreshold") Double memoryThreshold, + @Param("diskThreshold") Double diskThreshold); + + /** + * Find servers that haven't sent heartbeat recently + * @param orgUid organization UID + * @param cutoffTime cutoff time for heartbeat + * @return List + */ + @Query("SELECT s FROM ServerEntity s WHERE s.orgUid = :orgUid AND s.deleted = false " + + "AND (s.lastHeartbeat IS NULL OR s.lastHeartbeat < :cutoffTime)") + List findServersWithoutRecentHeartbeat(@Param("orgUid") String orgUid, + @Param("cutoffTime") LocalDateTime cutoffTime); + + /** + * Count servers by status + * @param status server status + * @param orgUid organization UID + * @return count + */ + long countByStatusAndOrgUidAndDeletedFalse(String status, String orgUid); + + /** + * Count servers by type + * @param type server type + * @param orgUid organization UID + * @return count + */ + long countByTypeAndOrgUidAndDeletedFalse(String type, String orgUid); + + /** + * Find servers by environment + * @param environment environment (DEV, TEST, PROD, etc.) + * @param orgUid organization UID + * @return List + */ + List findByEnvironmentAndOrgUidAndDeletedFalse(String environment, String orgUid); + + /** + * Find servers by location + * @param location server location + * @param orgUid organization UID + * @return List + */ + List findByLocationAndOrgUidAndDeletedFalse(String location, String orgUid); +} \ No newline at end of file diff --git a/modules/core/src/main/java/com/bytedesk/core/server/ServerRequest.java b/modules/core/src/main/java/com/bytedesk/core/server/ServerRequest.java new file mode 100644 index 0000000000..4a89bec0a3 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/server/ServerRequest.java @@ -0,0 +1,184 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-05-11 18:14:28 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-07-24 20:36:13 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.server; + +import com.bytedesk.core.base.BaseRequest; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import lombok.experimental.SuperBuilder; + +import java.time.LocalDateTime; + +/** + * Server request DTO + * Used for server monitoring operations + */ +@Data +@SuperBuilder +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@AllArgsConstructor +@NoArgsConstructor +public class ServerRequest extends BaseRequest { + + /** + * Server hostname or identifier + */ + private String serverName; + + /** + * Server IP address + */ + private String serverIp; + + /** + * Server type (APPLICATION, DATABASE, CACHE, etc.) + */ + @Builder.Default + private String serverType = ServerTypeEnum.APPLICATION.name(); + + /** + * Server status (ONLINE, OFFLINE, MAINTENANCE, etc.) + */ + @Builder.Default + private String serverStatus = ServerStatusEnum.ONLINE.name(); + + /** + * Server description + */ + private String description; + + /** + * CPU usage percentage (0-100) + */ + @Builder.Default + private Double cpuUsage = 0.0; + + /** + * Memory usage percentage (0-100) + */ + @Builder.Default + private Double memoryUsage = 0.0; + + /** + * Total memory in MB + */ + private Long totalMemoryMb; + + /** + * Used memory in MB + */ + private Long usedMemoryMb; + + /** + * Disk usage percentage (0-100) + */ + @Builder.Default + private Double diskUsage = 0.0; + + /** + * Total disk space in GB + */ + private Long totalDiskGb; + + /** + * Used disk space in GB + */ + private Long usedDiskGb; + + /** + * Server uptime in seconds + */ + private Long uptimeSeconds; + + /** + * Server start time + */ + private LocalDateTime startTime; + + /** + * Last heartbeat time + */ + private LocalDateTime lastHeartbeat; + + /** + * Server port (if applicable) + */ + private Integer serverPort; + + /** + * Operating system information + */ + private String osInfo; + + /** + * Java version (if applicable) + */ + private String javaVersion; + + /** + * Application version + */ + private String appVersion; + + /** + * Environment (DEV, TEST, PROD, etc.) + */ + @Builder.Default + private String environment = "DEV"; + + /** + * Server location or data center + */ + private String location; + + /** + * Monitoring enabled flag + */ + @Builder.Default + private Boolean monitoringEnabled = true; + + /** + * Alert threshold for CPU usage + */ + @Builder.Default + private Double cpuAlertThreshold = 80.0; + + /** + * Alert threshold for memory usage + */ + @Builder.Default + private Double memoryAlertThreshold = 80.0; + + /** + * Alert threshold for disk usage + */ + @Builder.Default + private Double diskAlertThreshold = 85.0; + + /** + * Search filters + */ + private String serverTypeFilter; + private String serverStatusFilter; + private String environmentFilter; + private String locationFilter; + private Boolean highUsageFilter; // true to show only servers with high resource usage + private Boolean offlineFilter; // true to show only offline servers + private Integer heartbeatThresholdMinutes; // minutes threshold for heartbeat check +} diff --git a/modules/core/src/main/java/com/bytedesk/core/server/ServerResponse.java b/modules/core/src/main/java/com/bytedesk/core/server/ServerResponse.java new file mode 100644 index 0000000000..48c599b581 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/server/ServerResponse.java @@ -0,0 +1,303 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-05-11 18:14:28 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-07-24 20:48:23 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.server; + +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; + +/** + * Server response DTO + * Used for server monitoring operations + */ +@Data +@SuperBuilder +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +@AllArgsConstructor +@NoArgsConstructor +public class ServerResponse extends BaseResponse { + + /** + * Server hostname or identifier + */ + private String serverName; + + /** + * Server IP address + */ + private String serverIp; + + /** + * Server type (APPLICATION, DATABASE, CACHE, etc.) + */ + private String serverType; + + /** + * Server type display name + */ + private String serverTypeDisplay; + + /** + * Server status (ONLINE, OFFLINE, MAINTENANCE, etc.) + */ + private String serverStatus; + + /** + * Server status display name + */ + private String serverStatusDisplay; + + /** + * Server description + */ + private String description; + + /** + * CPU usage percentage (0-100) + */ + private Double cpuUsage; + + /** + * Memory usage percentage (0-100) + */ + private Double memoryUsage; + + /** + * Total memory in MB + */ + private Long totalMemoryMb; + + /** + * Used memory in MB + */ + private Long usedMemoryMb; + + /** + * Disk usage percentage (0-100) + */ + private Double diskUsage; + + /** + * Total disk space in GB + */ + private Long totalDiskGb; + + /** + * Used disk space in GB + */ + private Long usedDiskGb; + + /** + * Server uptime in seconds + */ + private Long uptimeSeconds; + + /** + * Server uptime formatted string (e.g., "2 days 3 hours 45 minutes") + */ + // private String uptimeFormatted; + + /** + * Server start time + */ + private LocalDateTime startTime; + + /** + * Server start time formatted string + */ + private String startTimeFormatted; + + /** + * Last heartbeat time + */ + private LocalDateTime lastHeartbeat; + + /** + * Last heartbeat time formatted string + */ + private String lastHeartbeatFormatted; + + /** + * Server port (if applicable) + */ + private Integer serverPort; + + /** + * Operating system information + */ + private String osInfo; + + /** + * Java version (if applicable) + */ + private String javaVersion; + + /** + * Application version + */ + private String appVersion; + + /** + * Environment (DEV, TEST, PROD, etc.) + */ + private String environment; + + /** + * Server location or data center + */ + private String location; + + /** + * Monitoring enabled flag + */ + private Boolean monitoringEnabled; + + /** + * Alert threshold for CPU usage + */ + private Double cpuAlertThreshold; + + /** + * Alert threshold for memory usage + */ + private Double memoryAlertThreshold; + + /** + * Alert threshold for disk usage + */ + private Double diskAlertThreshold; + + /** + * Health status indicators + */ + private Boolean isHealthy; + private Boolean isOperational; + private Boolean hasHighCpuUsage; + private Boolean hasHighMemoryUsage; + private Boolean hasHighDiskUsage; + private Boolean hasRecentHeartbeat; + + /** + * Status color for UI display + */ + // private String statusColor; + + /** + * Status icon for UI display + */ + // private String statusIcon; + + /** + * Get formatted uptime string + * @return formatted uptime string + */ + public String getUptimeFormatted() { + if (uptimeSeconds == null || uptimeSeconds <= 0) { + return "Unknown"; + } + + long days = uptimeSeconds / 86400; + long hours = (uptimeSeconds % 86400) / 3600; + long minutes = (uptimeSeconds % 3600) / 60; + + if (days > 0) { + return String.format("%d days %d hours %d minutes", days, hours, minutes); + } else if (hours > 0) { + return String.format("%d hours %d minutes", hours, minutes); + } else { + return String.format("%d minutes", minutes); + } + } + + /** + * Get status color for UI display + * @return status color + */ + public String getStatusColor() { + if (serverStatus == null) { + return "gray"; + } + + switch (serverStatus) { + case "ONLINE": + return "green"; + case "OFFLINE": + return "red"; + case "MAINTENANCE": + return "orange"; + case "WARNING": + return "yellow"; + case "OVERLOADED": + return "red"; + case "ERROR": + return "red"; + case "STARTING": + case "STOPPING": + case "RESTARTING": + return "blue"; + default: + return "gray"; + } + } + + /** + * Get status icon for UI display + * @return status icon + */ + public String getStatusIcon() { + if (serverStatus == null) { + return "question-circle"; + } + + switch (serverStatus) { + case "ONLINE": + return "check-circle"; + case "OFFLINE": + return "times-circle"; + case "MAINTENANCE": + return "wrench"; + case "WARNING": + return "exclamation-triangle"; + case "OVERLOADED": + return "exclamation-circle"; + case "ERROR": + return "times-circle"; + case "STARTING": + return "play-circle"; + case "STOPPING": + return "stop-circle"; + case "RESTARTING": + return "sync"; + default: + return "question-circle"; + } + } + + /** + * Check if server has high resource usage + * @return true if any resource usage is high + */ + public Boolean getHasHighResourceUsage() { + return (cpuUsage != null && cpuUsage > 80) || + (memoryUsage != null && memoryUsage > 80) || + (diskUsage != null && diskUsage > 85); + } +} diff --git a/modules/core/src/main/java/com/bytedesk/core/server/ServerRestController.java b/modules/core/src/main/java/com/bytedesk/core/server/ServerRestController.java new file mode 100644 index 0000000000..82d16b7ba8 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/server/ServerRestController.java @@ -0,0 +1,124 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-05-11 18:25:36 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-07-24 20:43:52 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.server; + +import org.springframework.data.domain.Page; +import org.springframework.http.ResponseEntity; +// import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.context.annotation.Description; + +import com.bytedesk.core.annotation.ActionAnnotation; +import com.bytedesk.core.base.BaseRestController; +import com.bytedesk.core.utils.JsonResult; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import lombok.AllArgsConstructor; + +@RestController +@RequestMapping("/api/v1/server") +@AllArgsConstructor +@Tag(name = "Server Management", description = "Server management APIs for organizing and categorizing content with servers") +@Description("Server Management Controller - Content server and categorization APIs") +public class ServerRestController extends BaseRestController { + + private final ServerRestService serverRestService; + + // @PreAuthorize(RolePermissions.ROLE_ADMIN) + @ActionAnnotation(title = "标签", action = "组织查询", description = "query server by org") + @Operation(summary = "Query Servers by Organization", description = "Retrieve servers for the current organization") + @Override + public ResponseEntity queryByOrg(ServerRequest request) { + + Page servers = serverRestService.queryByOrg(request); + + return ResponseEntity.ok(JsonResult.success(servers)); + } + + @ActionAnnotation(title = "标签", action = "用户查询", description = "query server by user") + @Operation(summary = "Query Servers by User", description = "Retrieve servers for the current user") + @Override + public ResponseEntity queryByUser(ServerRequest request) { + + Page servers = serverRestService.queryByUser(request); + + return ResponseEntity.ok(JsonResult.success(servers)); + } + + @ActionAnnotation(title = "标签", action = "查询详情", description = "query server by uid") + @Operation(summary = "Query Server by UID", description = "Retrieve a specific server by its unique identifier") + @Override + public ResponseEntity queryByUid(ServerRequest request) { + + ServerResponse server = serverRestService.queryByUid(request); + + return ResponseEntity.ok(JsonResult.success(server)); + } + + @ActionAnnotation(title = "标签", action = "新建", description = "create server") + @Operation(summary = "Create Server", description = "Create a new server") + @Override + // @PreAuthorize("hasAuthority('TAG_CREATE')") + public ResponseEntity create(ServerRequest request) { + + ServerResponse server = serverRestService.create(request); + + return ResponseEntity.ok(JsonResult.success(server)); + } + + @ActionAnnotation(title = "标签", action = "更新", description = "update server") + @Operation(summary = "Update Server", description = "Update an existing server") + @Override + // @PreAuthorize("hasAuthority('TAG_UPDATE')") + public ResponseEntity update(ServerRequest request) { + + ServerResponse server = serverRestService.update(request); + + return ResponseEntity.ok(JsonResult.success(server)); + } + + @ActionAnnotation(title = "标签", action = "删除", description = "delete server") + @Operation(summary = "Delete Server", description = "Delete a server") + @Override + // @PreAuthorize("hasAuthority('TAG_DELETE')") + public ResponseEntity delete(ServerRequest request) { + + serverRestService.delete(request); + + return ResponseEntity.ok(JsonResult.success()); + } + + @ActionAnnotation(title = "标签", action = "导出", description = "export server") + @Operation(summary = "Export Servers", description = "Export servers to Excel format") + @Override + // @PreAuthorize("hasAuthority('TAG_EXPORT')") + @GetMapping("/export") + public Object export(ServerRequest request, HttpServletResponse response) { + return exportTemplate( + request, + response, + serverRestService, + ServerExcel.class, + "标签", + "server" + ); + } + + + +} \ No newline at end of file diff --git a/modules/core/src/main/java/com/bytedesk/core/server/ServerRestService.java b/modules/core/src/main/java/com/bytedesk/core/server/ServerRestService.java new file mode 100644 index 0000000000..89fbff41ea --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/server/ServerRestService.java @@ -0,0 +1,232 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-05-11 18:14:28 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-07-24 20:36:13 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.server; + +import com.bytedesk.core.base.BaseRestService; +import com.bytedesk.core.uid.UidUtils; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +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.orm.ObjectOptimisticLockingFailureException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.ZonedDateTime; +import java.util.List; +import java.util.Optional; + +/** + * REST service for server monitoring operations + * Provides CRUD operations and business logic for server management + */ +@Slf4j +@Service +@AllArgsConstructor +public class ServerRestService extends BaseRestService { + + private final ServerRepository serverRepository; + private final ServerService serverService; + private final ModelMapper modelMapper; + private final UidUtils uidUtils; + + // Helper method for internal use + private Page queryByOrgEntity(ServerRequest request) { + Pageable pageable = request.getPageable(); + // TODO: Implement ServerSpecification.search(request) + return serverRepository.findAll(pageable); + } + + @Override + public Page queryByOrg(ServerRequest request) { + Page page = queryByOrgEntity(request); + return page.map(this::convertToResponse); + } + + @Override + public Page queryByUser(ServerRequest request) { + // For server monitoring, user query is same as org query + return queryByOrg(request); + } + + @Override + public ServerResponse queryByUid(ServerRequest request) { + Optional optional = findByUid(request.getUid()); + if (optional.isPresent()) { + ServerEntity entity = optional.get(); + return convertToResponse(entity); + } else { + throw new RuntimeException("Server not found"); + } + } + + @Cacheable(value = "server", key = "#uid", unless="#result==null") + @Override + public Optional findByUid(String uid) { + return serverRepository.findByUid(uid); + } + + @Override + public ServerResponse create(ServerRequest request) { + ServerEntity entity = convertToEntity(request); + entity.setUid(uidUtils.getUid()); + entity.setCreatedAt(ZonedDateTime.now()); + entity.setUpdatedAt(ZonedDateTime.now()); + + ServerEntity savedEntity = serverService.createServer(entity); + return convertToResponse(savedEntity); + } + + @Override + public ServerResponse update(ServerRequest request) { + Optional optional = serverRepository.findByUid(request.getUid()); + if (optional.isPresent()) { + ServerEntity existingEntity = optional.get(); + updateEntityFromRequest(existingEntity, request); + existingEntity.setUpdatedAt(ZonedDateTime.now()); + + ServerEntity savedEntity = serverService.updateServer(existingEntity); + return convertToResponse(savedEntity); + } else { + throw new RuntimeException("Server not found"); + } + } + + @Override + public void deleteByUid(String uid) { + serverService.deleteServer(uid); + } + + @Override + public void delete(ServerRequest request) { + deleteByUid(request.getUid()); + } + + @Override + public ServerResponse convertToResponse(ServerEntity entity) { + ServerResponse response = modelMapper.map(entity, ServerResponse.class); + + // Set display names + if (entity.getType() != null) { + try { + ServerTypeEnum typeEnum = ServerTypeEnum.valueOf(entity.getType()); + response.setServerTypeDisplay(typeEnum.getChineseName()); + } catch (IllegalArgumentException e) { + response.setServerTypeDisplay(entity.getType()); + } + } + + if (entity.getStatus() != null) { + try { + ServerStatusEnum statusEnum = ServerStatusEnum.valueOf(entity.getStatus()); + response.setServerStatusDisplay(statusEnum.getChineseName()); + response.setIsHealthy(statusEnum.isHealthy()); + response.setIsOperational(statusEnum.isOperational()); + } catch (IllegalArgumentException e) { + response.setServerStatusDisplay(entity.getStatus()); + } + } + + // Set health indicators + response.setHasHighCpuUsage(entity.getCpuUsage() != null && entity.getCpuUsage() > 80); + response.setHasHighMemoryUsage(entity.getMemoryUsage() != null && entity.getMemoryUsage() > 80); + response.setHasHighDiskUsage(entity.getDiskUsage() != null && entity.getDiskUsage() > 85); + response.setHasRecentHeartbeat(entity.getLastHeartbeat() != null && + entity.getLastHeartbeat().isAfter(java.time.LocalDateTime.now().minusMinutes(5))); + + return response; + } + + @Override + protected String getUidFromRequest(ServerRequest request) { + return request.getUid(); + } + + @Override + public ServerEntity doSave(ServerEntity entity) { + return serverRepository.save(entity); + } + + @Override + protected ServerEntity handleOptimisticLockingFailureException(ObjectOptimisticLockingFailureException e, ServerEntity entity) { + log.warn("Optimistic locking failure for server: {}", entity.getUid()); + return entity; + } + + // Helper methods + private ServerEntity convertToEntity(ServerRequest request) { + ServerEntity entity = new ServerEntity(); + entity.setServerName(request.getServerName()); + entity.setServerIp(request.getServerIp()); + entity.setType(request.getServerType()); + entity.setStatus(request.getServerStatus()); + entity.setDescription(request.getDescription()); + entity.setCpuUsage(request.getCpuUsage()); + entity.setMemoryUsage(request.getMemoryUsage()); + entity.setTotalMemoryMb(request.getTotalMemoryMb()); + entity.setUsedMemoryMb(request.getUsedMemoryMb()); + entity.setDiskUsage(request.getDiskUsage()); + entity.setTotalDiskGb(request.getTotalDiskGb()); + entity.setUsedDiskGb(request.getUsedDiskGb()); + entity.setUptimeSeconds(request.getUptimeSeconds()); + entity.setStartTime(request.getStartTime()); + entity.setLastHeartbeat(request.getLastHeartbeat()); + entity.setServerPort(request.getServerPort()); + entity.setOsInfo(request.getOsInfo()); + entity.setJavaVersion(request.getJavaVersion()); + entity.setAppVersion(request.getAppVersion()); + entity.setEnvironment(request.getEnvironment()); + entity.setLocation(request.getLocation()); + entity.setMonitoringEnabled(request.getMonitoringEnabled()); + entity.setCpuAlertThreshold(request.getCpuAlertThreshold()); + entity.setMemoryAlertThreshold(request.getMemoryAlertThreshold()); + entity.setDiskAlertThreshold(request.getDiskAlertThreshold()); + entity.setOrgUid(request.getOrgUid()); + entity.setDeleted(false); + + return entity; + } + + private void updateEntityFromRequest(ServerEntity entity, ServerRequest request) { + if (request.getServerName() != null) { + entity.setServerName(request.getServerName()); + } + if (request.getServerType() != null) { + entity.setType(request.getServerType()); + } + if (request.getServerStatus() != null) { + entity.setStatus(request.getServerStatus()); + } + if (request.getDescription() != null) { + entity.setDescription(request.getDescription()); + } + if (request.getCpuUsage() != null) { + entity.setCpuUsage(request.getCpuUsage()); + } + if (request.getMemoryUsage() != null) { + entity.setMemoryUsage(request.getMemoryUsage()); + } + if (request.getDiskUsage() != null) { + entity.setDiskUsage(request.getDiskUsage()); + } + if (request.getEnvironment() != null) { + entity.setEnvironment(request.getEnvironment()); + } + if (request.getLocation() != null) { + entity.setLocation(request.getLocation()); + } + } +} \ No newline at end of file diff --git a/modules/core/src/main/java/com/bytedesk/core/server/ServerService.java b/modules/core/src/main/java/com/bytedesk/core/server/ServerService.java new file mode 100644 index 0000000000..406073f22a --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/server/ServerService.java @@ -0,0 +1,314 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-05-11 18:14:28 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-07-24 19:56:39 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.server; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; +import java.lang.management.OperatingSystemMXBean; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; + +/** + * Service for server monitoring and management + * Provides business logic for server monitoring operations + */ +@Slf4j +@Service +@AllArgsConstructor +public class ServerService { + + private final ServerRepository serverRepository; + + /** + * Create a new server record + * @param serverEntity server entity to create + * @return created server entity + */ + @Transactional + public ServerEntity createServer(ServerEntity serverEntity) { + log.info("Creating server: {}", serverEntity.getServerName()); + return serverRepository.save(serverEntity); + } + + /** + * Update server information + * @param serverEntity server entity to update + * @return updated server entity + */ + @Transactional + public ServerEntity updateServer(ServerEntity serverEntity) { + log.info("Updating server: {}", serverEntity.getServerName()); + return serverRepository.save(serverEntity); + } + + /** + * Find server by UID + * @param uid server UID + * @return Optional + */ + public Optional findByUid(String uid) { + return serverRepository.findByUid(uid); + } + + /** + * Find all servers for an organization + * @param orgUid organization UID + * @return List + */ + public List findByOrgUid(String orgUid) { + return serverRepository.findByOrgUidAndDeletedFalseOrderByCreatedAtDesc(orgUid); + } + + /** + * Find servers by type + * @param type server type + * @param orgUid organization UID + * @return List + */ + public List findByType(String type, String orgUid) { + return serverRepository.findByTypeAndOrgUidAndDeletedFalse(type, orgUid); + } + + /** + * Find servers by status + * @param status server status + * @param orgUid organization UID + * @return List + */ + public List findByStatus(String status, String orgUid) { + return serverRepository.findByStatusAndOrgUidAndDeletedFalse(status, orgUid); + } + + /** + * Find servers with high resource usage + * @param orgUid organization UID + * @return List + */ + public List findServersWithHighUsage(String orgUid) { + return serverRepository.findServersWithHighUsage(orgUid, 80.0, 80.0, 85.0); + } + + /** + * Find servers without recent heartbeat + * @param orgUid organization UID + * @param minutesThreshold minutes threshold for heartbeat + * @return List + */ + public List findServersWithoutRecentHeartbeat(String orgUid, int minutesThreshold) { + LocalDateTime cutoffTime = LocalDateTime.now().minusMinutes(minutesThreshold); + return serverRepository.findServersWithoutRecentHeartbeat(orgUid, cutoffTime); + } + + /** + * Update server heartbeat + * @param uid server UID + * @return updated server entity + */ + @Transactional + public Optional updateHeartbeat(String uid) { + Optional optional = serverRepository.findByUid(uid); + if (optional.isPresent()) { + ServerEntity server = optional.get(); + server.setLastHeartbeat(LocalDateTime.now()); + return Optional.of(serverRepository.save(server)); + } + return Optional.empty(); + } + + /** + * Update server resource usage + * @param uid server UID + * @param cpuUsage CPU usage percenservere + * @param memoryUsage memory usage percenservere + * @param diskUsage disk usage percenservere + * @return updated server entity + */ + @Transactional + public Optional updateResourceUsage(String uid, Double cpuUsage, Double memoryUsage, Double diskUsage) { + Optional optional = serverRepository.findByUid(uid); + if (optional.isPresent()) { + ServerEntity server = optional.get(); + server.setCpuUsage(cpuUsage); + server.setMemoryUsage(memoryUsage); + server.setDiskUsage(diskUsage); + server.setLastHeartbeat(LocalDateTime.now()); + + // Update status based on resource usage + if (cpuUsage > 90 || memoryUsage > 90 || diskUsage > 95) { + server.setStatus(ServerStatusEnum.OVERLOADED.name()); + } else if (cpuUsage > 80 || memoryUsage > 80 || diskUsage > 85) { + server.setStatus(ServerStatusEnum.WARNING.name()); + } else { + server.setStatus(ServerStatusEnum.ONLINE.name()); + } + + return Optional.of(serverRepository.save(server)); + } + return Optional.empty(); + } + + /** + * Get current server metrics (for self-monitoring) + * @return ServerEntity with current metrics + */ + public ServerEntity getCurrentServerMetrics() { + OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean(); + MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean(); + + // Calculate memory usage + long totalMemory = memoryBean.getHeapMemoryUsage().getMax(); + long usedMemory = memoryBean.getHeapMemoryUsage().getUsed(); + double memoryUsagePercent = (double) usedMemory / totalMemory * 100; + + // Get CPU usage (approximate) + double cpuUsage = osBean.getSystemLoadAverage() * 100; + if (cpuUsage > 100) cpuUsage = 100; + + // Create server entity with current metrics + return ServerEntity.builder() + .serverName(System.getProperty("os.name") + " - " + System.getProperty("user.name")) + .serverIp("127.0.0.1") + .type(ServerTypeEnum.APPLICATION.name()) + .status(ServerStatusEnum.ONLINE.name()) + .cpuUsage(cpuUsage) + .memoryUsage(memoryUsagePercent) + .totalMemoryMb(totalMemory / (1024 * 1024)) + .usedMemoryMb(usedMemory / (1024 * 1024)) + .uptimeSeconds(ManagementFactory.getRuntimeMXBean().getUptime() / 1000) + .startTime(LocalDateTime.now().minusSeconds(ManagementFactory.getRuntimeMXBean().getUptime() / 1000)) + .lastHeartbeat(LocalDateTime.now()) + .osInfo(System.getProperty("os.name") + " " + System.getProperty("os.version")) + .javaVersion(System.getProperty("java.version")) + .environment("DEV") + .monitoringEnabled(true) + .build(); + } + + /** + * Delete server (soft delete) + * @param uid server UID + */ + @Transactional + public void deleteServer(String uid) { + Optional optional = serverRepository.findByUid(uid); + if (optional.isPresent()) { + ServerEntity server = optional.get(); + server.setDeleted(true); + serverRepository.save(server); + log.info("Deleted server: {}", server.getServerName()); + } + } + + /** + * Get server statistics for an organization + * @param orgUid organization UID + * @return ServerStatistics object + */ + public ServerStatistics getServerStatistics(String orgUid) { + List servers = findByOrgUid(orgUid); + + long totalServers = servers.size(); + long onlineServers = servers.stream() + .filter(s -> ServerStatusEnum.ONLINE.name().equals(s.getStatus())) + .count(); + long offlineServers = servers.stream() + .filter(s -> ServerStatusEnum.OFFLINE.name().equals(s.getStatus())) + .count(); + long warningServers = servers.stream() + .filter(s -> ServerStatusEnum.WARNING.name().equals(s.getStatus())) + .count(); + long overloadedServers = servers.stream() + .filter(s -> ServerStatusEnum.OVERLOADED.name().equals(s.getStatus())) + .count(); + + return ServerStatistics.builder() + .totalServers(totalServers) + .onlineServers(onlineServers) + .offlineServers(offlineServers) + .warningServers(warningServers) + .overloadedServers(overloadedServers) + .build(); + } + + /** + * Server statistics data class + */ + public static class ServerStatistics { + private long totalServers; + private long onlineServers; + private long offlineServers; + private long warningServers; + private long overloadedServers; + + // Getters and setters + public long getTotalServers() { return totalServers; } + public void setTotalServers(long totalServers) { this.totalServers = totalServers; } + + public long getOnlineServers() { return onlineServers; } + public void setOnlineServers(long onlineServers) { this.onlineServers = onlineServers; } + + public long getOfflineServers() { return offlineServers; } + public void setOfflineServers(long offlineServers) { this.offlineServers = offlineServers; } + + public long getWarningServers() { return warningServers; } + public void setWarningServers(long warningServers) { this.warningServers = warningServers; } + + public long getOverloadedServers() { return overloadedServers; } + public void setOverloadedServers(long overloadedServers) { this.overloadedServers = overloadedServers; } + + // Builder pattern + public static ServerStatisticsBuilder builder() { + return new ServerStatisticsBuilder(); + } + + public static class ServerStatisticsBuilder { + private ServerStatistics statistics = new ServerStatistics(); + + public ServerStatisticsBuilder totalServers(long totalServers) { + statistics.totalServers = totalServers; + return this; + } + + public ServerStatisticsBuilder onlineServers(long onlineServers) { + statistics.onlineServers = onlineServers; + return this; + } + + public ServerStatisticsBuilder offlineServers(long offlineServers) { + statistics.offlineServers = offlineServers; + return this; + } + + public ServerStatisticsBuilder warningServers(long warningServers) { + statistics.warningServers = warningServers; + return this; + } + + public ServerStatisticsBuilder overloadedServers(long overloadedServers) { + statistics.overloadedServers = overloadedServers; + return this; + } + + public ServerStatistics build() { + return statistics; + } + } + } +} \ No newline at end of file diff --git a/modules/core/src/main/java/com/bytedesk/core/host/HostSpecification.java b/modules/core/src/main/java/com/bytedesk/core/server/ServerSpecification.java similarity index 56% rename from modules/core/src/main/java/com/bytedesk/core/host/HostSpecification.java rename to modules/core/src/main/java/com/bytedesk/core/server/ServerSpecification.java index 75dfee7551..97b2ce5a36 100644 --- a/modules/core/src/main/java/com/bytedesk/core/host/HostSpecification.java +++ b/modules/core/src/main/java/com/bytedesk/core/server/ServerSpecification.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-07-09 22:19:21 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2025-07-22 17:14:52 + * @LastEditTime: 2025-07-24 20:39:01 * @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. @@ -11,13 +11,12 @@ * 联系:270580156@qq.com * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.core.host; +package com.bytedesk.core.server; 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; @@ -25,30 +24,14 @@ import jakarta.persistence.criteria.Predicate; import lombok.extern.slf4j.Slf4j; @Slf4j -public class HostSpecification extends BaseSpecification { +public class ServerSpecification extends BaseSpecification { - public static Specification search(HostRequest request) { + public static Specification search(ServerRequest request) { log.info("request: {} orgUid: {} pageNumber: {} pageSize: {}", request, request.getOrgUid(), request.getPageNumber(), request.getPageSize()); return (root, query, criteriaBuilder) -> { List predicates = new ArrayList<>(); predicates.addAll(getBasicPredicates(root, criteriaBuilder, request.getOrgUid())); - // name - if (StringUtils.hasText(request.getName())) { - predicates.add(criteriaBuilder.like(root.get("name"), "%" + request.getName() + "%")); - } - // description - if (StringUtils.hasText(request.getDescription())) { - predicates.add(criteriaBuilder.like(root.get("description"), "%" + request.getDescription() + "%")); - } - // type - if (StringUtils.hasText(request.getType())) { - predicates.add(criteriaBuilder.equal(root.get("type"), request.getType())); - } - // - if (StringUtils.hasText(request.getUserUid())) { - predicates.add(criteriaBuilder.equal(root.get("userUid"), request.getUserUid())); - } // return criteriaBuilder.and(predicates.toArray(new Predicate[0])); }; diff --git a/modules/core/src/main/java/com/bytedesk/core/server/ServerStatusEnum.java b/modules/core/src/main/java/com/bytedesk/core/server/ServerStatusEnum.java new file mode 100644 index 0000000000..71489f6be7 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/server/ServerStatusEnum.java @@ -0,0 +1,95 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-05-11 18:14:28 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-07-24 19:56:39 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.server; + +/** + * Server status enumeration + * Defines different statuses of servers + */ +public enum ServerStatusEnum { + ONLINE, // 在线运行 + OFFLINE, // 离线 + MAINTENANCE, // 维护中 + STARTING, // 启动中 + STOPPING, // 停止中 + RESTARTING, // 重启中 + ERROR, // 错误状态 + WARNING, // 警告状态 + DEGRADED, // 降级运行 + OVERLOADED, // 过载 + UNKNOWN; // 未知状态 + + /** + * Get Chinese name for the server status + * @return Chinese name + */ + public String getChineseName() { + switch (this) { + case ONLINE: + return "在线运行"; + case OFFLINE: + return "离线"; + case MAINTENANCE: + return "维护中"; + case STARTING: + return "启动中"; + case STOPPING: + return "停止中"; + case RESTARTING: + return "重启中"; + case ERROR: + return "错误状态"; + case WARNING: + return "警告状态"; + case DEGRADED: + return "降级运行"; + case OVERLOADED: + return "过载"; + case UNKNOWN: + return "未知状态"; + default: + return this.name(); + } + } + + /** + * Check if server is healthy (online and not in error/warning states) + * @return true if healthy + */ + public boolean isHealthy() { + return this == ONLINE || this == DEGRADED; + } + + /** + * Check if server is operational (can serve requests) + * @return true if operational + */ + public boolean isOperational() { + return this == ONLINE || this == DEGRADED || this == OVERLOADED; + } + + /** + * Convert from string value to enum + * @param value string value + * @return ServerStatusEnum + */ + public static ServerStatusEnum fromValue(String value) { + for (ServerStatusEnum status : ServerStatusEnum.values()) { + if (status.name().equalsIgnoreCase(value)) { + return status; + } + } + throw new IllegalArgumentException("No ServerStatusEnum constant with value: " + value); + } +} \ No newline at end of file diff --git a/modules/core/src/main/java/com/bytedesk/core/server/ServerTypeEnum.java b/modules/core/src/main/java/com/bytedesk/core/server/ServerTypeEnum.java new file mode 100644 index 0000000000..3e49bd2f53 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/server/ServerTypeEnum.java @@ -0,0 +1,97 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2024-05-11 18:14:28 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-07-24 19:56:39 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * 联系:270580156@qq.com + * Copyright (c) 2024 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.server; + +/** + * Server type enumeration + * Defines different types of servers in the system + */ +public enum ServerTypeEnum { + APPLICATION, // 应用服务器 + DATABASE, // 数据库服务器 + CACHE, // 缓存服务器 + LOAD_BALANCER, // 负载均衡器 + WEB_SERVER, // Web服务器 + FILE_SERVER, // 文件服务器 + MAIL_SERVER, // 邮件服务器 + DNS_SERVER, // DNS服务器 + PROXY_SERVER, // 代理服务器 + MONITORING, // 监控服务器 + BACKUP, // 备份服务器 + GATEWAY, // 网关服务器 + API_SERVER, // API服务器 + MESSAGE_QUEUE, // 消息队列服务器 + SEARCH_ENGINE, // 搜索引擎服务器 + CDN, // CDN服务器 + OTHER; // 其他类型 + + /** + * Get Chinese name for the server type + * @return Chinese name + */ + public String getChineseName() { + switch (this) { + case APPLICATION: + return "应用服务器"; + case DATABASE: + return "数据库服务器"; + case CACHE: + return "缓存服务器"; + case LOAD_BALANCER: + return "负载均衡器"; + case WEB_SERVER: + return "Web服务器"; + case FILE_SERVER: + return "文件服务器"; + case MAIL_SERVER: + return "邮件服务器"; + case DNS_SERVER: + return "DNS服务器"; + case PROXY_SERVER: + return "代理服务器"; + case MONITORING: + return "监控服务器"; + case BACKUP: + return "备份服务器"; + case GATEWAY: + return "网关服务器"; + case API_SERVER: + return "API服务器"; + case MESSAGE_QUEUE: + return "消息队列服务器"; + case SEARCH_ENGINE: + return "搜索引擎服务器"; + case CDN: + return "CDN服务器"; + case OTHER: + return "其他类型"; + default: + return this.name(); + } + } + + /** + * Convert from string value to enum + * @param value string value + * @return ServerTypeEnum + */ + public static ServerTypeEnum fromValue(String value) { + for (ServerTypeEnum type : ServerTypeEnum.values()) { + if (type.name().equalsIgnoreCase(value)) { + return type; + } + } + throw new IllegalArgumentException("No ServerTypeEnum constant with value: " + value); + } +} \ No newline at end of file diff --git a/modules/core/src/main/java/com/bytedesk/core/host/event/HostCreateEvent.java b/modules/core/src/main/java/com/bytedesk/core/server/event/ServerCreateEvent.java similarity index 75% rename from modules/core/src/main/java/com/bytedesk/core/host/event/HostCreateEvent.java rename to modules/core/src/main/java/com/bytedesk/core/server/event/ServerCreateEvent.java index 25b7350800..07c86a8324 100644 --- a/modules/core/src/main/java/com/bytedesk/core/host/event/HostCreateEvent.java +++ b/modules/core/src/main/java/com/bytedesk/core/server/event/ServerCreateEvent.java @@ -11,26 +11,26 @@ * * Copyright (c) 2025 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.core.host.event; +package com.bytedesk.core.server.event; import org.springframework.context.ApplicationEvent; -import com.bytedesk.core.host.HostEntity; +import com.bytedesk.core.server.ServerEntity; import lombok.Data; import lombok.EqualsAndHashCode; @Data @EqualsAndHashCode(callSuper = false) -public class HostCreateEvent extends ApplicationEvent { +public class ServerCreateEvent extends ApplicationEvent { private static final long serialVersionUID = 1L; - private HostEntity host; + private ServerEntity server; - public HostCreateEvent(HostEntity host) { - super(host); - this.host = host; + public ServerCreateEvent(ServerEntity server) { + super(server); + this.server = server; } } diff --git a/modules/core/src/main/java/com/bytedesk/core/host/event/HostDeleteEvent.java b/modules/core/src/main/java/com/bytedesk/core/server/event/ServerDeleteEvent.java similarity index 75% rename from modules/core/src/main/java/com/bytedesk/core/host/event/HostDeleteEvent.java rename to modules/core/src/main/java/com/bytedesk/core/server/event/ServerDeleteEvent.java index ad8e930ca3..8b051f5702 100644 --- a/modules/core/src/main/java/com/bytedesk/core/host/event/HostDeleteEvent.java +++ b/modules/core/src/main/java/com/bytedesk/core/server/event/ServerDeleteEvent.java @@ -11,25 +11,25 @@ * * Copyright (c) 2025 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.core.host.event; +package com.bytedesk.core.server.event; import org.springframework.context.ApplicationEvent; -import com.bytedesk.core.host.HostEntity; +import com.bytedesk.core.server.ServerEntity; import lombok.Data; import lombok.EqualsAndHashCode; @Data @EqualsAndHashCode(callSuper = false) -public class HostDeleteEvent extends ApplicationEvent { +public class ServerDeleteEvent extends ApplicationEvent { private static final long serialVersionUID = 1L; - private HostEntity host; + private ServerEntity server; - public HostDeleteEvent(HostEntity host) { - super(host); - this.host = host; + public ServerDeleteEvent(ServerEntity server) { + super(server); + this.server = server; } } diff --git a/modules/core/src/main/java/com/bytedesk/core/host/event/HostUpdateEvent.java b/modules/core/src/main/java/com/bytedesk/core/server/event/ServerUpdateEvent.java similarity index 75% rename from modules/core/src/main/java/com/bytedesk/core/host/event/HostUpdateEvent.java rename to modules/core/src/main/java/com/bytedesk/core/server/event/ServerUpdateEvent.java index bbd0fda643..45da2fc9c6 100644 --- a/modules/core/src/main/java/com/bytedesk/core/host/event/HostUpdateEvent.java +++ b/modules/core/src/main/java/com/bytedesk/core/server/event/ServerUpdateEvent.java @@ -11,26 +11,26 @@ * * Copyright (c) 2025 by bytedesk.com, All Rights Reserved. */ -package com.bytedesk.core.host.event; +package com.bytedesk.core.server.event; import org.springframework.context.ApplicationEvent; -import com.bytedesk.core.host.HostEntity; +import com.bytedesk.core.server.ServerEntity; import lombok.Data; import lombok.EqualsAndHashCode; @Data @EqualsAndHashCode(callSuper = false) -public class HostUpdateEvent extends ApplicationEvent { +public class ServerUpdateEvent extends ApplicationEvent { private static final long serialVersionUID = 1L; - private HostEntity host; + private ServerEntity server; - public HostUpdateEvent(HostEntity host) { - super(host); - this.host = host; + public ServerUpdateEvent(ServerEntity server) { + super(server); + this.server = server; } } diff --git a/modules/core/src/main/java/com/bytedesk/core/host/package-info.java b/modules/core/src/main/java/com/bytedesk/core/server/package-info.java similarity index 63% rename from modules/core/src/main/java/com/bytedesk/core/host/package-info.java rename to modules/core/src/main/java/com/bytedesk/core/server/package-info.java index 8cb0330e2e..7311f8e886 100644 --- a/modules/core/src/main/java/com/bytedesk/core/host/package-info.java +++ b/modules/core/src/main/java/com/bytedesk/core/server/package-info.java @@ -1,5 +1,5 @@ @NonNullApi -package com.bytedesk.core.host; +package com.bytedesk.core.server; import org.springframework.lang.NonNullApi;