From 79f8b4c30e0ccb7858ce4d004391416d6f4c43ed Mon Sep 17 00:00:00 2001 From: jack ning Date: Mon, 6 Oct 2025 08:02:33 +0800 Subject: [PATCH] update --- .../java/com/bytedesk/ai/config/AiPlugin.java | 87 +++++ .../com/bytedesk/call/config/CallPlugin.java | 87 +++++ .../bytedesk/call/ivr/CallIvrRestService.java | 28 +- .../core/plugin/AbstractBytedeskPlugin.java | 127 +++++++ .../bytedesk/core/plugin/BytedeskPlugin.java | 116 ++++++ .../com/bytedesk/core/plugin/CorePlugin.java | 85 +++++ .../core/plugin/PluginController.java | 227 +++++++++++ .../bytedesk/core/plugin/PluginRegistry.java | 284 ++++++++++++++ .../java/com/bytedesk/core/plugin/README.md | 353 ++++++++++++++++++ .../bytedesk/kbase/config/KbasePlugin.java | 87 +++++ .../service/config/ServicePlugin.java | 87 +++++ .../bytedesk/ticket/config/TicketPlugin.java | 87 +++++ .../com/bytedesk/voc/config/VocPlugin.java | 87 +++++ 13 files changed, 1726 insertions(+), 16 deletions(-) create mode 100644 modules/ai/src/main/java/com/bytedesk/ai/config/AiPlugin.java create mode 100644 modules/call/src/main/java/com/bytedesk/call/config/CallPlugin.java create mode 100644 modules/core/src/main/java/com/bytedesk/core/plugin/AbstractBytedeskPlugin.java create mode 100644 modules/core/src/main/java/com/bytedesk/core/plugin/BytedeskPlugin.java create mode 100644 modules/core/src/main/java/com/bytedesk/core/plugin/CorePlugin.java create mode 100644 modules/core/src/main/java/com/bytedesk/core/plugin/PluginController.java create mode 100644 modules/core/src/main/java/com/bytedesk/core/plugin/PluginRegistry.java create mode 100644 modules/core/src/main/java/com/bytedesk/core/plugin/README.md create mode 100644 modules/kbase/src/main/java/com/bytedesk/kbase/config/KbasePlugin.java create mode 100644 modules/service/src/main/java/com/bytedesk/service/config/ServicePlugin.java create mode 100644 modules/ticket/src/main/java/com/bytedesk/ticket/config/TicketPlugin.java create mode 100644 modules/voc/src/main/java/com/bytedesk/voc/config/VocPlugin.java diff --git a/modules/ai/src/main/java/com/bytedesk/ai/config/AiPlugin.java b/modules/ai/src/main/java/com/bytedesk/ai/config/AiPlugin.java new file mode 100644 index 0000000000..9a323ba7f8 --- /dev/null +++ b/modules/ai/src/main/java/com/bytedesk/ai/config/AiPlugin.java @@ -0,0 +1,87 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2025-10-06 10:00:00 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-10-06 10:00:00 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * + * Copyright (c) 2025 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.ai.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.stereotype.Component; + +import com.bytedesk.core.plugin.AbstractBytedeskPlugin; + +import lombok.extern.slf4j.Slf4j; + +/** + * AI模块插件 + * 提供大模型集成、智能客服、对话生成等功能 + */ +@Slf4j +@Component +public class AiPlugin extends AbstractBytedeskPlugin { + + @Value("${bytedesk.ai.enabled:true}") + private boolean enabled; + + @Value("${bytedesk.ai.version:1.0.0}") + private String version; + + @Autowired(required = false) + private HealthIndicator aiHealthIndicator; + + @Override + protected HealthIndicator getHealthIndicator() { + return aiHealthIndicator; + } + + @Override + public String getPluginId() { + return "ai"; + } + + @Override + public String getPluginName() { + return "AI Assistant"; + } + + @Override + public String getDescription() { + return "AI智能助手,提供大模型集成、智能客服、对话生成、语义理解等功能"; + } + + @Override + public String getVersion() { + return version; + } + + @Override + public boolean isEnabled() { + return enabled; + } + + @Override + public int getPriority() { + return 15; // AI功能优先级较高 + } + + @Override + public String[] getDependencies() { + return new String[]{"core", "kbase"}; // 依赖核心模块和知识库 + } + + @Override + public void initialize() { + super.initialize(); + log.info("AI Assistant Plugin initialized - Features: LLM Integration, Intelligent Q&A, Semantic Understanding"); + } +} diff --git a/modules/call/src/main/java/com/bytedesk/call/config/CallPlugin.java b/modules/call/src/main/java/com/bytedesk/call/config/CallPlugin.java new file mode 100644 index 0000000000..94e1339b78 --- /dev/null +++ b/modules/call/src/main/java/com/bytedesk/call/config/CallPlugin.java @@ -0,0 +1,87 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2025-10-06 10:00:00 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-10-06 10:00:00 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * + * Copyright (c) 2025 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.call.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.stereotype.Component; + +import com.bytedesk.core.plugin.AbstractBytedeskPlugin; + +import lombok.extern.slf4j.Slf4j; + +/** + * 呼叫中心模块插件 + * 提供语音通话、FreeSWITCH集成等功能 + */ +@Slf4j +@Component +public class CallPlugin extends AbstractBytedeskPlugin { + + @Value("${bytedesk.call.enabled:false}") + private boolean enabled; + + @Value("${bytedesk.call.version:1.0.0}") + private String version; + + @Autowired(required = false) + private HealthIndicator callHealthIndicator; + + @Override + protected HealthIndicator getHealthIndicator() { + return callHealthIndicator; + } + + @Override + public String getPluginId() { + return "call"; + } + + @Override + public String getPluginName() { + return "Call Center"; + } + + @Override + public String getDescription() { + return "呼叫中心系统,提供语音通话、FreeSWITCH集成、通话记录等功能"; + } + + @Override + public String getVersion() { + return version; + } + + @Override + public boolean isEnabled() { + return enabled; + } + + @Override + public int getPriority() { + return 40; // 呼叫中心优先级中等 + } + + @Override + public String[] getDependencies() { + return new String[]{"core"}; // 依赖核心模块 + } + + @Override + public void initialize() { + super.initialize(); + log.info("Call Center Plugin initialized - Features: Voice Call, FreeSWITCH Integration, Call Recording"); + } +} diff --git a/modules/call/src/main/java/com/bytedesk/call/ivr/CallIvrRestService.java b/modules/call/src/main/java/com/bytedesk/call/ivr/CallIvrRestService.java index bdffe1499f..95236513c4 100644 --- a/modules/call/src/main/java/com/bytedesk/call/ivr/CallIvrRestService.java +++ b/modules/call/src/main/java/com/bytedesk/call/ivr/CallIvrRestService.java @@ -25,14 +25,10 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import com.bytedesk.core.base.BaseRestServiceWithExport; -import com.bytedesk.core.constant.BytedeskConsts; import com.bytedesk.core.constant.I18Consts; -import com.bytedesk.core.enums.LevelEnum; import com.bytedesk.core.exception.NotLoginException; 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; @@ -213,18 +209,18 @@ public class CallIvrRestService extends BaseRestServiceWithExport getHealthStatus() { + Map status = new HashMap<>(); + + try { + HealthIndicator healthIndicator = getHealthIndicator(); + if (healthIndicator != null) { + Health health = healthIndicator.health(); + status.put("status", health.getStatus().getCode()); + status.put("details", health.getDetails()); + } else { + status.put("status", "UP"); + status.put("message", "No health indicator configured"); + } + } catch (Exception e) { + log.error("Failed to get health status for plugin: {}", getPluginId(), e); + status.put("status", "DOWN"); + status.put("error", e.getMessage()); + } + + return status; + } + + /** + * 获取插件统计信息 + * 包含基本信息和运行时长 + */ + @Override + public Map getStatistics() { + Map stats = new HashMap<>(); + stats.put("pluginId", getPluginId()); + stats.put("pluginName", getPluginName()); + stats.put("version", getVersion()); + stats.put("enabled", isEnabled()); + stats.put("priority", getPriority()); + stats.put("dependencies", getDependencies()); + + // 计算运行时长 + Duration uptime = Duration.between(registerTime, Instant.now()); + stats.put("uptime-seconds", uptime.getSeconds()); + stats.put("uptime-readable", formatDuration(uptime)); + stats.put("registerTime", registerTime.toString()); + + return stats; + } + + /** + * 格式化时长 + */ + protected String formatDuration(Duration duration) { + long days = duration.toDays(); + long hours = duration.toHoursPart(); + long minutes = duration.toMinutesPart(); + return String.format("%dd %dh %dm", days, hours, minutes); + } + + /** + * 插件初始化 + */ + @Override + public void initialize() { + log.info("Initializing plugin: {} ({})", getPluginName(), getPluginId()); + } + + /** + * 插件销毁 + */ + @Override + public void destroy() { + log.info("Destroying plugin: {} ({})", getPluginName(), getPluginId()); + } +} diff --git a/modules/core/src/main/java/com/bytedesk/core/plugin/BytedeskPlugin.java b/modules/core/src/main/java/com/bytedesk/core/plugin/BytedeskPlugin.java new file mode 100644 index 0000000000..9dbd9549c0 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/plugin/BytedeskPlugin.java @@ -0,0 +1,116 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2025-10-06 10:00:00 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-10-06 10:00:00 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * + * Copyright (c) 2025 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.plugin; + +import java.util.Map; + +/** + * Bytedesk插件接口 + * 所有模块插件必须实现此接口 + */ +public interface BytedeskPlugin { + + /** + * 获取插件唯一标识符 + * @return 插件ID,如:kbase, service, ticket, ai, call, voc + */ + String getPluginId(); + + /** + * 获取插件名称 + * @return 插件显示名称 + */ + String getPluginName(); + + /** + * 获取插件描述 + * @return 插件功能描述 + */ + String getDescription(); + + /** + * 获取插件版本 + * @return 版本号 + */ + String getVersion(); + + /** + * 获取插件作者 + * @return 作者信息 + */ + String getAuthor(); + + /** + * 获取插件官网 + * @return 官网URL + */ + default String getWebsite() { + return "https://bytedesk.com"; + } + + /** + * 插件是否启用 + * @return true表示启用,false表示禁用 + */ + boolean isEnabled(); + + /** + * 插件优先级(数字越小优先级越高) + * @return 优先级值,默认100 + */ + default int getPriority() { + return 100; + } + + /** + * 获取插件依赖的其他插件ID列表 + * @return 依赖的插件ID数组,如果没有依赖返回空数组 + */ + default String[] getDependencies() { + return new String[0]; + } + + /** + * 获取插件健康状态 + * @return 健康状态信息 + */ + Map getHealthStatus(); + + /** + * 获取插件统计信息 + * @return 统计数据 + */ + default Map getStatistics() { + return Map.of( + "enabled", isEnabled(), + "version", getVersion() + ); + } + + /** + * 插件初始化 + * 在插件注册后调用 + */ + default void initialize() { + // 默认空实现 + } + + /** + * 插件销毁 + * 在应用关闭时调用 + */ + default void destroy() { + // 默认空实现 + } +} diff --git a/modules/core/src/main/java/com/bytedesk/core/plugin/CorePlugin.java b/modules/core/src/main/java/com/bytedesk/core/plugin/CorePlugin.java new file mode 100644 index 0000000000..8d67ba2056 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/plugin/CorePlugin.java @@ -0,0 +1,85 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2025-10-06 10:00:00 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-10-06 10:00:00 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * + * Copyright (c) 2025 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.plugin; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.stereotype.Component; + +import lombok.extern.slf4j.Slf4j; + +/** + * 核心模块插件 + * 提供基础功能:用户管理、权限控制、消息系统等 + */ +@Slf4j +@Component +public class CorePlugin extends AbstractBytedeskPlugin { + + @Value("${bytedesk.core.enabled:true}") + private boolean enabled; + + @Value("${bytedesk.core.version:1.0.0}") + private String version; + + @Autowired(required = false) + private HealthIndicator coreHealthIndicator; + + @Override + protected HealthIndicator getHealthIndicator() { + return coreHealthIndicator; + } + + @Override + public String getPluginId() { + return "core"; + } + + @Override + public String getPluginName() { + return "Core Module"; + } + + @Override + public String getDescription() { + return "核心模块,提供用户管理、权限控制、消息系统、通知等基础功能"; + } + + @Override + public String getVersion() { + return version; + } + + @Override + public boolean isEnabled() { + return enabled; + } + + @Override + public int getPriority() { + return 1; // 核心模块优先级最高 + } + + @Override + public String[] getDependencies() { + return new String[0]; // 核心模块不依赖其他模块 + } + + @Override + public void initialize() { + super.initialize(); + log.info("Core Module Plugin initialized - Features: User Management, RBAC, Messaging, Notification"); + } +} diff --git a/modules/core/src/main/java/com/bytedesk/core/plugin/PluginController.java b/modules/core/src/main/java/com/bytedesk/core/plugin/PluginController.java new file mode 100644 index 0000000000..711db7545f --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/plugin/PluginController.java @@ -0,0 +1,227 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2025-10-06 10:00:00 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-10-06 10:00:00 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * + * Copyright (c) 2025 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.plugin; + +import java.util.*; +import java.util.stream.Collectors; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +/** + * 插件管理控制器 + * 提供插件信息查询和管理接口 + */ +@Slf4j +@RestController +@RequestMapping("/api/v1/plugins") +@RequiredArgsConstructor +@Tag(name = "Plugin Management", description = "插件管理接口") +public class PluginController { + + private final PluginRegistry pluginRegistry; + + /** + * 获取所有插件列表 + * http://127.0.0.1:9003/api/v1/plugins + */ + @GetMapping + @Operation(summary = "获取所有插件", description = "获取系统中所有已注册的插件列表") + public ResponseEntity> getAllPlugins() { + List plugins = pluginRegistry.getAllPlugins(); + + List> pluginList = plugins.stream() + .sorted(Comparator.comparingInt(BytedeskPlugin::getPriority)) + .map(this::convertPluginToMap) + .collect(Collectors.toList()); + + Map response = new LinkedHashMap<>(); + response.put("total", plugins.size()); + response.put("enabled", pluginRegistry.getEnabledPluginCount()); + response.put("plugins", pluginList); + + return ResponseEntity.ok(response); + } + + /** + * 获取已启用的插件列表 + */ + @GetMapping("/enabled") + @Operation(summary = "获取已启用插件", description = "获取系统中所有已启用的插件列表") + public ResponseEntity> getEnabledPlugins() { + List plugins = pluginRegistry.getEnabledPlugins(); + + List> pluginList = plugins.stream() + .map(this::convertPluginToMap) + .collect(Collectors.toList()); + + Map response = new LinkedHashMap<>(); + response.put("total", plugins.size()); + response.put("plugins", pluginList); + + return ResponseEntity.ok(response); + } + + /** + * 获取指定插件信息 + */ + @GetMapping("/{pluginId}") + @Operation(summary = "获取插件详情", description = "根据插件ID获取插件详细信息") + public ResponseEntity> getPlugin(@PathVariable String pluginId) { + Optional plugin = pluginRegistry.getPlugin(pluginId); + + if (plugin.isEmpty()) { + return ResponseEntity.notFound().build(); + } + + Map response = convertPluginToDetailMap(plugin.get()); + return ResponseEntity.ok(response); + } + + /** + * 获取插件健康状态 + */ + @GetMapping("/{pluginId}/health") + @Operation(summary = "获取插件健康状态", description = "获取指定插件的健康检查状态") + public ResponseEntity> getPluginHealth(@PathVariable String pluginId) { + Optional plugin = pluginRegistry.getPlugin(pluginId); + + if (plugin.isEmpty()) { + return ResponseEntity.notFound().build(); + } + + Map health = plugin.get().getHealthStatus(); + return ResponseEntity.ok(health); + } + + /** + * 获取所有插件的健康状态 + */ + @GetMapping("/health") + @Operation(summary = "获取所有插件健康状态", description = "获取系统中所有插件的健康检查状态") + public ResponseEntity> getAllPluginsHealth() { + Map> healthStatus = pluginRegistry.getAllPluginsHealthStatus(); + + Map response = new LinkedHashMap<>(); + response.put("timestamp", System.currentTimeMillis()); + response.put("total", pluginRegistry.getPluginCount()); + response.put("enabled", pluginRegistry.getEnabledPluginCount()); + response.put("plugins", healthStatus); + + return ResponseEntity.ok(response); + } + + /** + * 获取插件统计信息 + */ + @GetMapping("/{pluginId}/statistics") + @Operation(summary = "获取插件统计信息", description = "获取指定插件的统计数据") + public ResponseEntity> getPluginStatistics(@PathVariable String pluginId) { + Optional plugin = pluginRegistry.getPlugin(pluginId); + + if (plugin.isEmpty()) { + return ResponseEntity.notFound().build(); + } + + Map statistics = plugin.get().getStatistics(); + return ResponseEntity.ok(statistics); + } + + /** + * 获取所有插件的统计信息 + */ + @GetMapping("/statistics") + @Operation(summary = "获取所有插件统计信息", description = "获取系统中所有插件的统计数据") + public ResponseEntity> getAllPluginsStatistics() { + Map> statistics = pluginRegistry.getAllPluginsStatistics(); + + Map response = new LinkedHashMap<>(); + response.put("timestamp", System.currentTimeMillis()); + response.put("total", pluginRegistry.getPluginCount()); + response.put("enabled", pluginRegistry.getEnabledPluginCount()); + response.put("plugins", statistics); + + return ResponseEntity.ok(response); + } + + /** + * 获取插件概览信息 + */ + @GetMapping("/overview") + @Operation(summary = "获取插件概览", description = "获取插件系统的概览信息") + public ResponseEntity> getPluginsOverview() { + Map overview = new LinkedHashMap<>(); + + // 基本统计 + overview.put("totalPlugins", pluginRegistry.getPluginCount()); + overview.put("enabledPlugins", pluginRegistry.getEnabledPluginCount()); + overview.put("disabledPlugins", pluginRegistry.getPluginCount() - pluginRegistry.getEnabledPluginCount()); + + // 插件列表(简化信息) + List> pluginSummary = pluginRegistry.getAllPlugins().stream() + .sorted(Comparator.comparingInt(BytedeskPlugin::getPriority)) + .map(plugin -> { + Map summary = new LinkedHashMap<>(); + summary.put("id", plugin.getPluginId()); + summary.put("name", plugin.getPluginName()); + summary.put("version", plugin.getVersion()); + summary.put("enabled", plugin.isEnabled()); + summary.put("priority", plugin.getPriority()); + return summary; + }) + .collect(Collectors.toList()); + + overview.put("plugins", pluginSummary); + + return ResponseEntity.ok(overview); + } + + /** + * 转换插件为Map(简化版) + */ + private Map convertPluginToMap(BytedeskPlugin plugin) { + Map map = new LinkedHashMap<>(); + map.put("id", plugin.getPluginId()); + map.put("name", plugin.getPluginName()); + map.put("description", plugin.getDescription()); + map.put("version", plugin.getVersion()); + map.put("enabled", plugin.isEnabled()); + map.put("priority", plugin.getPriority()); + return map; + } + + /** + * 转换插件为Map(详细版) + */ + private Map convertPluginToDetailMap(BytedeskPlugin plugin) { + Map map = new LinkedHashMap<>(); + map.put("id", plugin.getPluginId()); + map.put("name", plugin.getPluginName()); + map.put("description", plugin.getDescription()); + map.put("version", plugin.getVersion()); + map.put("author", plugin.getAuthor()); + map.put("website", plugin.getWebsite()); + map.put("enabled", plugin.isEnabled()); + map.put("priority", plugin.getPriority()); + map.put("dependencies", plugin.getDependencies()); + map.put("statistics", plugin.getStatistics()); + map.put("health", plugin.getHealthStatus()); + return map; + } +} diff --git a/modules/core/src/main/java/com/bytedesk/core/plugin/PluginRegistry.java b/modules/core/src/main/java/com/bytedesk/core/plugin/PluginRegistry.java new file mode 100644 index 0000000000..991bc97dd3 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/plugin/PluginRegistry.java @@ -0,0 +1,284 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2025-10-06 10:00:00 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-10-06 10:00:00 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * + * Copyright (c) 2025 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.plugin; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +import org.springframework.stereotype.Component; + +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; +import lombok.extern.slf4j.Slf4j; + +/** + * 插件注册中心 + * 管理所有Bytedesk模块插件的注册、查询和生命周期 + */ +@Slf4j +@Component +public class PluginRegistry { + + private final Map plugins = new ConcurrentHashMap<>(); + private final List pluginList; + + public PluginRegistry(List pluginList) { + this.pluginList = pluginList; + } + + /** + * 初始化:自动注册所有插件 + */ + @PostConstruct + public void init() { + log.info("Initializing Plugin Registry..."); + + // 按优先级排序 + List sortedPlugins = pluginList.stream() + .sorted(Comparator.comparingInt(BytedeskPlugin::getPriority)) + .collect(Collectors.toList()); + + // 注册所有插件 + for (BytedeskPlugin plugin : sortedPlugins) { + registerPlugin(plugin); + } + + log.info("Plugin Registry initialized with {} plugins", plugins.size()); + logRegisteredPlugins(); + } + + /** + * 注册插件 + */ + public void registerPlugin(BytedeskPlugin plugin) { + if (plugin == null) { + log.warn("Attempted to register null plugin"); + return; + } + + String pluginId = plugin.getPluginId(); + if (pluginId == null || pluginId.trim().isEmpty()) { + log.error("Plugin ID cannot be null or empty"); + return; + } + + // 检查依赖 + if (!checkDependencies(plugin)) { + log.error("Plugin {} has unmet dependencies", pluginId); + return; + } + + // 注册插件 + plugins.put(pluginId, plugin); + log.info("Registered plugin: {} ({}) - Version: {}, Enabled: {}", + plugin.getPluginName(), pluginId, plugin.getVersion(), plugin.isEnabled()); + + // 初始化插件 + try { + plugin.initialize(); + } catch (Exception e) { + log.error("Failed to initialize plugin: {}", pluginId, e); + } + } + + /** + * 注销插件 + */ + public void unregisterPlugin(String pluginId) { + BytedeskPlugin plugin = plugins.remove(pluginId); + if (plugin != null) { + try { + plugin.destroy(); + log.info("Unregistered plugin: {} ({})", plugin.getPluginName(), pluginId); + } catch (Exception e) { + log.error("Failed to destroy plugin: {}", pluginId, e); + } + } + } + + /** + * 获取插件 + */ + public Optional getPlugin(String pluginId) { + return Optional.ofNullable(plugins.get(pluginId)); + } + + /** + * 获取所有插件 + */ + public List getAllPlugins() { + return new ArrayList<>(plugins.values()); + } + + /** + * 获取所有已启用的插件 + */ + public List getEnabledPlugins() { + return plugins.values().stream() + .filter(BytedeskPlugin::isEnabled) + .sorted(Comparator.comparingInt(BytedeskPlugin::getPriority)) + .collect(Collectors.toList()); + } + + /** + * 获取插件数量 + */ + public int getPluginCount() { + return plugins.size(); + } + + /** + * 获取已启用插件数量 + */ + public int getEnabledPluginCount() { + return (int) plugins.values().stream() + .filter(BytedeskPlugin::isEnabled) + .count(); + } + + /** + * 检查插件是否存在 + */ + public boolean hasPlugin(String pluginId) { + return plugins.containsKey(pluginId); + } + + /** + * 检查插件是否启用 + */ + public boolean isPluginEnabled(String pluginId) { + return getPlugin(pluginId) + .map(BytedeskPlugin::isEnabled) + .orElse(false); + } + + /** + * 获取所有插件的健康状态 + */ + public Map> getAllPluginsHealthStatus() { + Map> healthStatus = new LinkedHashMap<>(); + + plugins.values().stream() + .sorted(Comparator.comparingInt(BytedeskPlugin::getPriority)) + .forEach(plugin -> { + try { + healthStatus.put(plugin.getPluginId(), plugin.getHealthStatus()); + } catch (Exception e) { + log.error("Failed to get health status for plugin: {}", plugin.getPluginId(), e); + healthStatus.put(plugin.getPluginId(), Map.of( + "status", "ERROR", + "error", e.getMessage() + )); + } + }); + + return healthStatus; + } + + /** + * 获取所有插件的统计信息 + */ + public Map> getAllPluginsStatistics() { + Map> statistics = new LinkedHashMap<>(); + + plugins.values().stream() + .sorted(Comparator.comparingInt(BytedeskPlugin::getPriority)) + .forEach(plugin -> { + try { + statistics.put(plugin.getPluginId(), plugin.getStatistics()); + } catch (Exception e) { + log.error("Failed to get statistics for plugin: {}", plugin.getPluginId(), e); + } + }); + + return statistics; + } + + /** + * 检查插件依赖是否满足 + */ + private boolean checkDependencies(BytedeskPlugin plugin) { + String[] dependencies = plugin.getDependencies(); + if (dependencies == null || dependencies.length == 0) { + return true; + } + + for (String dependency : dependencies) { + if (!plugins.containsKey(dependency)) { + log.warn("Plugin {} depends on {}, but it's not registered yet", + plugin.getPluginId(), dependency); + return false; + } + } + + return true; + } + + /** + * 记录已注册的插件信息 + */ + private void logRegisteredPlugins() { + if (plugins.isEmpty()) { + log.warn("No plugins registered"); + return; + } + + log.info("=================================================="); + log.info("Registered Plugins ({})", plugins.size()); + log.info("=================================================="); + + plugins.values().stream() + .sorted(Comparator.comparingInt(BytedeskPlugin::getPriority)) + .forEach(plugin -> { + log.info(" - {} ({}) v{} [{}] Priority: {}", + plugin.getPluginName(), + plugin.getPluginId(), + plugin.getVersion(), + plugin.isEnabled() ? "ENABLED" : "DISABLED", + plugin.getPriority()); + + String[] deps = plugin.getDependencies(); + if (deps != null && deps.length > 0) { + log.info(" Dependencies: {}", String.join(", ", deps)); + } + }); + + log.info("=================================================="); + } + + /** + * 销毁:注销所有插件 + */ + @PreDestroy + public void destroy() { + log.info("Destroying Plugin Registry..."); + + // 按优先级逆序销毁 + List sortedPlugins = new ArrayList<>(plugins.values()); + sortedPlugins.sort(Comparator.comparingInt(BytedeskPlugin::getPriority).reversed()); + + for (BytedeskPlugin plugin : sortedPlugins) { + try { + plugin.destroy(); + log.info("Destroyed plugin: {}", plugin.getPluginId()); + } catch (Exception e) { + log.error("Failed to destroy plugin: {}", plugin.getPluginId(), e); + } + } + + plugins.clear(); + log.info("Plugin Registry destroyed"); + } +} diff --git a/modules/core/src/main/java/com/bytedesk/core/plugin/README.md b/modules/core/src/main/java/com/bytedesk/core/plugin/README.md new file mode 100644 index 0000000000..abd8a4f206 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/plugin/README.md @@ -0,0 +1,353 @@ +# Bytedesk 插件系统 + +## 概述 + +Bytedesk 插件系统提供了一个统一的框架来管理各个功能模块,实现模块化架构和集中管理。 + +## 架构设计 + +### 核心组件 + +1. **BytedeskPlugin 接口** - 定义插件的基本契约 +2. **AbstractBytedeskPlugin** - 提供插件的通用实现 +3. **PluginRegistry** - 插件注册中心,管理所有插件 +4. **PluginController** - REST API 接口,提供插件信息查询 + +### 插件生命周期 + +``` +注册 -> 初始化 -> 运行 -> 销毁 +``` + +## 已注册插件 + +| 插件ID | 名称 | 描述 | 优先级 | 依赖 | +|--------|------|------|--------|------| +| service | Customer Service | 在线客服系统 | 10 | core | +| ai | AI Assistant | AI智能助手 | 15 | core, kbase | +| kbase | Knowledge Base | 知识库管理 | 20 | core | +| ticket | Ticket System | 工单管理系统 | 30 | core | +| call | Call Center | 呼叫中心 | 40 | core | +| voc | Voice of Customer | 客户之声 | 50 | core | + +## API 接口 + +### 1. 获取所有插件 + +```http +GET /api/v1/plugins +``` + +**响应示例:** +```json +{ + "total": 6, + "enabled": 5, + "plugins": [ + { + "id": "service", + "name": "Customer Service", + "description": "在线客服系统,提供实时聊天、会话管理、客服分配、消息队列等功能", + "version": "1.0.0", + "enabled": true, + "priority": 10 + } + ] +} +``` + +### 2. 获取插件概览 + +```http +GET /api/v1/plugins/overview +``` + +### 3. 获取插件详情 + +```http +GET /api/v1/plugins/{pluginId} +``` + +### 4. 获取插件健康状态 + +```http +GET /api/v1/plugins/{pluginId}/health +``` + +### 5. 获取所有插件健康状态 + +```http +GET /api/v1/plugins/health +``` + +### 6. 获取插件统计信息 + +```http +GET /api/v1/plugins/{pluginId}/statistics +``` + +### 7. 获取所有插件统计信息 + +```http +GET /api/v1/plugins/statistics +``` + +## 创建自定义插件 + +### 步骤 1: 创建插件类 + +```java +package com.bytedesk.yourmodule.plugin; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.stereotype.Component; +import com.bytedesk.core.plugin.AbstractBytedeskPlugin; + +@Component +public class YourModulePlugin extends AbstractBytedeskPlugin { + + @Value("${bytedesk.yourmodule.enabled:true}") + private boolean enabled; + + @Value("${bytedesk.yourmodule.version:1.0.0}") + private String version; + + @Autowired(required = false) + private HealthIndicator yourModuleHealthIndicator; + + @Override + protected HealthIndicator getHealthIndicator() { + return yourModuleHealthIndicator; + } + + @Override + public String getPluginId() { + return "yourmodule"; + } + + @Override + public String getPluginName() { + return "Your Module Name"; + } + + @Override + public String getDescription() { + return "Your module description"; + } + + @Override + public String getVersion() { + return version; + } + + @Override + public boolean isEnabled() { + return enabled; + } + + @Override + public int getPriority() { + return 100; // 设置优先级 + } + + @Override + public String[] getDependencies() { + return new String[]{"core"}; // 设置依赖 + } +} +``` + +### 步骤 2: 配置文件 + +在 `application.yml` 中添加: + +```yaml +bytedesk: + yourmodule: + enabled: true + version: 1.0.0 +``` + +### 步骤 3: 创建健康检查器(可选) + +```java +@Slf4j +@Component +public class YourModuleHealthIndicator implements HealthIndicator { + + @Override + public Health health() { + try { + // 执行健康检查逻辑 + return Health.up() + .withDetail("status", "Running") + .build(); + } catch (Exception e) { + return Health.down() + .withDetail("error", e.getMessage()) + .build(); + } + } +} +``` + +## 插件优先级 + +优先级数值越小,优先级越高。建议范围: + +- **1-10**: 核心功能(如 service) +- **11-20**: 重要功能(如 ai) +- **21-30**: 常规功能(如 kbase) +- **31-50**: 辅助功能(如 ticket, call, voc) +- **51-100**: 扩展功能 + +## 插件依赖 + +插件可以声明依赖其他插件: + +```java +@Override +public String[] getDependencies() { + return new String[]{"core", "kbase"}; +} +``` + +插件注册中心会在注册时检查依赖是否满足。 + +## 配置选项 + +### 启用/禁用插件 + +```yaml +bytedesk: + kbase: + enabled: true # 启用知识库插件 + call: + enabled: false # 禁用呼叫中心插件 +``` + +### 设置插件版本 + +```yaml +bytedesk: + service: + version: 1.2.0 +``` + +## 监控和管理 + +### 通过 Actuator 端点 + +```bash +# 查看应用健康状态(包含所有模块) +curl http://localhost:9003/actuator/health + +# 查看特定模块健康状态 +curl http://localhost:9003/actuator/health/kbase +curl http://localhost:9003/actuator/health/service +``` + +### 通过插件 API + +```bash +# 查看插件概览 +curl http://localhost:9003/api/v1/plugins/overview + +# 查看所有插件健康状态 +curl http://localhost:9003/api/v1/plugins/health + +# 查看特定插件详情 +curl http://localhost:9003/api/v1/plugins/kbase +``` + +## 最佳实践 + +1. **命名规范** + - 插件ID:使用小写字母,如 `kbase`, `service` + - 类名:使用 PascalCase + Plugin 后缀,如 `KbasePlugin` + +2. **版本管理** + - 使用语义化版本号:`major.minor.patch` + - 在配置文件中集中管理版本 + +3. **健康检查** + - 为每个插件提供健康检查器 + - 检查关键资源(数据库、缓存、外部服务等) + +4. **依赖管理** + - 明确声明插件依赖 + - 避免循环依赖 + +5. **错误处理** + - 插件初始化失败不应影响其他插件 + - 提供详细的错误信息和日志 + +## 故障排查 + +### 插件未注册 + +**问题:** 插件列表中看不到某个插件 + +**排查步骤:** +1. 检查插件类是否添加了 `@Component` 注解 +2. 检查插件类所在包是否被扫描 +3. 查看启动日志中的插件注册信息 +4. 检查插件是否被配置为禁用状态 + +### 依赖检查失败 + +**问题:** 插件因依赖问题注册失败 + +**排查步骤:** +1. 检查依赖的插件是否已注册 +2. 查看插件注册顺序(按优先级) +3. 确认依赖关系是否正确 + +### 健康检查失败 + +**问题:** 插件健康状态为 DOWN + +**排查步骤:** +1. 检查 HealthIndicator 实现 +2. 查看健康检查错误日志 +3. 确认相关资源(数据库、Redis 等)是否正常 + +## 示例代码 + +### 获取插件信息 + +```java +@Autowired +private PluginRegistry pluginRegistry; + +// 获取所有插件 +List plugins = pluginRegistry.getAllPlugins(); + +// 获取特定插件 +Optional plugin = pluginRegistry.getPlugin("kbase"); + +// 检查插件是否启用 +boolean isEnabled = pluginRegistry.isPluginEnabled("service"); + +// 获取插件健康状态 +Map health = plugin.get().getHealthStatus(); +``` + +## 未来扩展 + +- [ ] 插件热加载/卸载 +- [ ] 插件配置动态更新 +- [ ] 插件间通信机制 +- [ ] 插件市场 +- [ ] 插件权限管理 + +## 相关文档 + +- [Health Indicator 文档](https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.endpoints.health) +- [Spring Boot Actuator](https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html) +- [模块化架构设计](./ARCHITECTURE.md) + +## 许可证 + +Business Source License 1.1 - https://github.com/Bytedesk/bytedesk/blob/main/LICENSE diff --git a/modules/kbase/src/main/java/com/bytedesk/kbase/config/KbasePlugin.java b/modules/kbase/src/main/java/com/bytedesk/kbase/config/KbasePlugin.java new file mode 100644 index 0000000000..39b9e96ab4 --- /dev/null +++ b/modules/kbase/src/main/java/com/bytedesk/kbase/config/KbasePlugin.java @@ -0,0 +1,87 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2025-10-06 10:00:00 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-10-06 10:00:00 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * + * Copyright (c) 2025 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.kbase.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.stereotype.Component; + +import com.bytedesk.core.plugin.AbstractBytedeskPlugin; + +import lombok.extern.slf4j.Slf4j; + +/** + * 知识库模块插件 + * 提供知识库、文章、FAQ、向量检索等功能 + */ +@Slf4j +@Component +public class KbasePlugin extends AbstractBytedeskPlugin { + + @Value("${bytedesk.kbase.enabled:true}") + private boolean enabled; + + @Value("${bytedesk.kbase.version:1.0.0}") + private String version; + + @Autowired(required = false) + private HealthIndicator kbaseHealthIndicator; + + @Override + protected HealthIndicator getHealthIndicator() { + return kbaseHealthIndicator; + } + + @Override + public String getPluginId() { + return "kbase"; + } + + @Override + public String getPluginName() { + return "Knowledge Base"; + } + + @Override + public String getDescription() { + return "知识库管理系统,提供文章、FAQ、智能问答、向量检索等功能"; + } + + @Override + public String getVersion() { + return version; + } + + @Override + public boolean isEnabled() { + return enabled; + } + + @Override + public int getPriority() { + return 20; // 知识库优先级较高 + } + + @Override + public String[] getDependencies() { + return new String[]{"core"}; // 依赖核心模块 + } + + @Override + public void initialize() { + super.initialize(); + log.info("Knowledge Base Plugin initialized - Features: Article, FAQ, Vector Search, AI Integration"); + } +} diff --git a/modules/service/src/main/java/com/bytedesk/service/config/ServicePlugin.java b/modules/service/src/main/java/com/bytedesk/service/config/ServicePlugin.java new file mode 100644 index 0000000000..726fe4440c --- /dev/null +++ b/modules/service/src/main/java/com/bytedesk/service/config/ServicePlugin.java @@ -0,0 +1,87 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2025-10-06 10:00:00 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-10-06 10:00:00 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * + * Copyright (c) 2025 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.service.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.stereotype.Component; + +import com.bytedesk.core.plugin.AbstractBytedeskPlugin; + +import lombok.extern.slf4j.Slf4j; + +/** + * 在线客服模块插件 + * 提供实时聊天、会话管理、客服分配等功能 + */ +@Slf4j +@Component +public class ServicePlugin extends AbstractBytedeskPlugin { + + @Value("${bytedesk.service.enabled:true}") + private boolean enabled; + + @Value("${bytedesk.service.version:1.0.0}") + private String version; + + @Autowired(required = false) + private HealthIndicator serviceHealthIndicator; + + @Override + protected HealthIndicator getHealthIndicator() { + return serviceHealthIndicator; + } + + @Override + public String getPluginId() { + return "service"; + } + + @Override + public String getPluginName() { + return "Customer Service"; + } + + @Override + public String getDescription() { + return "在线客服系统,提供实时聊天、会话管理、客服分配、消息队列等功能"; + } + + @Override + public String getVersion() { + return version; + } + + @Override + public boolean isEnabled() { + return enabled; + } + + @Override + public int getPriority() { + return 10; // 客服是核心功能,优先级最高 + } + + @Override + public String[] getDependencies() { + return new String[]{"core"}; // 依赖核心模块 + } + + @Override + public void initialize() { + super.initialize(); + log.info("Customer Service Plugin initialized - Features: Live Chat, Session Management, Agent Assignment"); + } +} diff --git a/modules/ticket/src/main/java/com/bytedesk/ticket/config/TicketPlugin.java b/modules/ticket/src/main/java/com/bytedesk/ticket/config/TicketPlugin.java new file mode 100644 index 0000000000..c766a9a006 --- /dev/null +++ b/modules/ticket/src/main/java/com/bytedesk/ticket/config/TicketPlugin.java @@ -0,0 +1,87 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2025-10-06 10:00:00 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-10-06 10:00:00 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * + * Copyright (c) 2025 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.ticket.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.stereotype.Component; + +import com.bytedesk.core.plugin.AbstractBytedeskPlugin; + +import lombok.extern.slf4j.Slf4j; + +/** + * 工单系统模块插件 + * 提供工单管理、工单流转、SLA等功能 + */ +@Slf4j +@Component +public class TicketPlugin extends AbstractBytedeskPlugin { + + @Value("${bytedesk.ticket.enabled:true}") + private boolean enabled; + + @Value("${bytedesk.ticket.version:1.0.0}") + private String version; + + @Autowired(required = false) + private HealthIndicator ticketHealthIndicator; + + @Override + protected HealthIndicator getHealthIndicator() { + return ticketHealthIndicator; + } + + @Override + public String getPluginId() { + return "ticket"; + } + + @Override + public String getPluginName() { + return "Ticket System"; + } + + @Override + public String getDescription() { + return "工单管理系统,提供工单创建、分配、流转、SLA管理等功能"; + } + + @Override + public String getVersion() { + return version; + } + + @Override + public boolean isEnabled() { + return enabled; + } + + @Override + public int getPriority() { + return 30; // 工单系统优先级中等 + } + + @Override + public String[] getDependencies() { + return new String[]{"core"}; // 依赖核心模块 + } + + @Override + public void initialize() { + super.initialize(); + log.info("Ticket System Plugin initialized - Features: Ticket Management, Workflow, SLA"); + } +} diff --git a/modules/voc/src/main/java/com/bytedesk/voc/config/VocPlugin.java b/modules/voc/src/main/java/com/bytedesk/voc/config/VocPlugin.java new file mode 100644 index 0000000000..577df5e5be --- /dev/null +++ b/modules/voc/src/main/java/com/bytedesk/voc/config/VocPlugin.java @@ -0,0 +1,87 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2025-10-06 10:00:00 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-10-06 10:00:00 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * + * Copyright (c) 2025 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.voc.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.stereotype.Component; + +import com.bytedesk.core.plugin.AbstractBytedeskPlugin; + +import lombok.extern.slf4j.Slf4j; + +/** + * 客户之声模块插件 + * 提供客户反馈、满意度调查、数据分析等功能 + */ +@Slf4j +@Component +public class VocPlugin extends AbstractBytedeskPlugin { + + @Value("${bytedesk.voc.enabled:true}") + private boolean enabled; + + @Value("${bytedesk.voc.version:1.0.0}") + private String version; + + @Autowired(required = false) + private HealthIndicator vocHealthIndicator; + + @Override + protected HealthIndicator getHealthIndicator() { + return vocHealthIndicator; + } + + @Override + public String getPluginId() { + return "voc"; + } + + @Override + public String getPluginName() { + return "Voice of Customer"; + } + + @Override + public String getDescription() { + return "客户之声系统,提供客户反馈收集、满意度调查、数据分析、报表生成等功能"; + } + + @Override + public String getVersion() { + return version; + } + + @Override + public boolean isEnabled() { + return enabled; + } + + @Override + public int getPriority() { + return 50; // VOC优先级较低 + } + + @Override + public String[] getDependencies() { + return new String[]{"core"}; // 依赖核心模块 + } + + @Override + public void initialize() { + super.initialize(); + log.info("Voice of Customer Plugin initialized - Features: Feedback Collection, Satisfaction Survey, Analytics"); + } +}