mirror of
https://gitee.com/270580156/weiyu.git
synced 2025-12-30 02:42:25 +00:00
update
This commit is contained in:
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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<CallIvrEntity,
|
||||
|
||||
public void initCallIvrs(String orgUid) {
|
||||
// log.info("initThreadCallIvr");
|
||||
for (String tag : CallIvrInitData.getAllCallIvrs()) {
|
||||
CallIvrRequest tagRequest = CallIvrRequest.builder()
|
||||
.uid(Utils.formatUid(orgUid, tag))
|
||||
.name(tag)
|
||||
.order(0)
|
||||
.type(CallIvrTypeEnum.THREAD.name())
|
||||
.level(LevelEnum.ORGANIZATION.name())
|
||||
.platform(BytedeskConsts.PLATFORM_BYTEDESK)
|
||||
.orgUid(orgUid)
|
||||
.build();
|
||||
create(tagRequest);
|
||||
}
|
||||
// for (String tag : CallIvrInitData.getAllCallIvrs()) {
|
||||
// CallIvrRequest tagRequest = CallIvrRequest.builder()
|
||||
// .uid(Utils.formatUid(orgUid, tag))
|
||||
// .name(tag)
|
||||
// .order(0)
|
||||
// .type(CallIvrTypeEnum.THREAD.name())
|
||||
// .level(LevelEnum.ORGANIZATION.name())
|
||||
// .platform(BytedeskConsts.PLATFORM_BYTEDESK)
|
||||
// .orgUid(orgUid)
|
||||
// .build();
|
||||
// create(tagRequest);
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* @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.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* Bytedesk插件抽象基类
|
||||
* 提供插件的通用实现
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class AbstractBytedeskPlugin implements BytedeskPlugin {
|
||||
|
||||
private final Instant registerTime = Instant.now();
|
||||
|
||||
/**
|
||||
* 获取插件作者
|
||||
* 默认返回Bytedesk团队
|
||||
*/
|
||||
@Override
|
||||
public String getAuthor() {
|
||||
return "Bytedesk Team";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取健康指示器
|
||||
* 子类可以重写此方法提供自定义的HealthIndicator
|
||||
*/
|
||||
protected HealthIndicator getHealthIndicator() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取插件健康状态
|
||||
* 默认从对应的HealthIndicator获取
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> getHealthStatus() {
|
||||
Map<String, Object> 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<String, Object> getStatistics() {
|
||||
Map<String, Object> 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());
|
||||
}
|
||||
}
|
||||
@@ -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<String, Object> getHealthStatus();
|
||||
|
||||
/**
|
||||
* 获取插件统计信息
|
||||
* @return 统计数据
|
||||
*/
|
||||
default Map<String, Object> getStatistics() {
|
||||
return Map.of(
|
||||
"enabled", isEnabled(),
|
||||
"version", getVersion()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 插件初始化
|
||||
* 在插件注册后调用
|
||||
*/
|
||||
default void initialize() {
|
||||
// 默认空实现
|
||||
}
|
||||
|
||||
/**
|
||||
* 插件销毁
|
||||
* 在应用关闭时调用
|
||||
*/
|
||||
default void destroy() {
|
||||
// 默认空实现
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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<Map<String, Object>> getAllPlugins() {
|
||||
List<BytedeskPlugin> plugins = pluginRegistry.getAllPlugins();
|
||||
|
||||
List<Map<String, Object>> pluginList = plugins.stream()
|
||||
.sorted(Comparator.comparingInt(BytedeskPlugin::getPriority))
|
||||
.map(this::convertPluginToMap)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Map<String, Object> 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<Map<String, Object>> getEnabledPlugins() {
|
||||
List<BytedeskPlugin> plugins = pluginRegistry.getEnabledPlugins();
|
||||
|
||||
List<Map<String, Object>> pluginList = plugins.stream()
|
||||
.map(this::convertPluginToMap)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Map<String, Object> response = new LinkedHashMap<>();
|
||||
response.put("total", plugins.size());
|
||||
response.put("plugins", pluginList);
|
||||
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定插件信息
|
||||
*/
|
||||
@GetMapping("/{pluginId}")
|
||||
@Operation(summary = "获取插件详情", description = "根据插件ID获取插件详细信息")
|
||||
public ResponseEntity<Map<String, Object>> getPlugin(@PathVariable String pluginId) {
|
||||
Optional<BytedeskPlugin> plugin = pluginRegistry.getPlugin(pluginId);
|
||||
|
||||
if (plugin.isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
Map<String, Object> response = convertPluginToDetailMap(plugin.get());
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取插件健康状态
|
||||
*/
|
||||
@GetMapping("/{pluginId}/health")
|
||||
@Operation(summary = "获取插件健康状态", description = "获取指定插件的健康检查状态")
|
||||
public ResponseEntity<Map<String, Object>> getPluginHealth(@PathVariable String pluginId) {
|
||||
Optional<BytedeskPlugin> plugin = pluginRegistry.getPlugin(pluginId);
|
||||
|
||||
if (plugin.isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
Map<String, Object> health = plugin.get().getHealthStatus();
|
||||
return ResponseEntity.ok(health);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有插件的健康状态
|
||||
*/
|
||||
@GetMapping("/health")
|
||||
@Operation(summary = "获取所有插件健康状态", description = "获取系统中所有插件的健康检查状态")
|
||||
public ResponseEntity<Map<String, Object>> getAllPluginsHealth() {
|
||||
Map<String, Map<String, Object>> healthStatus = pluginRegistry.getAllPluginsHealthStatus();
|
||||
|
||||
Map<String, Object> 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<Map<String, Object>> getPluginStatistics(@PathVariable String pluginId) {
|
||||
Optional<BytedeskPlugin> plugin = pluginRegistry.getPlugin(pluginId);
|
||||
|
||||
if (plugin.isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
Map<String, Object> statistics = plugin.get().getStatistics();
|
||||
return ResponseEntity.ok(statistics);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有插件的统计信息
|
||||
*/
|
||||
@GetMapping("/statistics")
|
||||
@Operation(summary = "获取所有插件统计信息", description = "获取系统中所有插件的统计数据")
|
||||
public ResponseEntity<Map<String, Object>> getAllPluginsStatistics() {
|
||||
Map<String, Map<String, Object>> statistics = pluginRegistry.getAllPluginsStatistics();
|
||||
|
||||
Map<String, Object> 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<Map<String, Object>> getPluginsOverview() {
|
||||
Map<String, Object> overview = new LinkedHashMap<>();
|
||||
|
||||
// 基本统计
|
||||
overview.put("totalPlugins", pluginRegistry.getPluginCount());
|
||||
overview.put("enabledPlugins", pluginRegistry.getEnabledPluginCount());
|
||||
overview.put("disabledPlugins", pluginRegistry.getPluginCount() - pluginRegistry.getEnabledPluginCount());
|
||||
|
||||
// 插件列表(简化信息)
|
||||
List<Map<String, Object>> pluginSummary = pluginRegistry.getAllPlugins().stream()
|
||||
.sorted(Comparator.comparingInt(BytedeskPlugin::getPriority))
|
||||
.map(plugin -> {
|
||||
Map<String, Object> 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<String, Object> convertPluginToMap(BytedeskPlugin plugin) {
|
||||
Map<String, Object> 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<String, Object> convertPluginToDetailMap(BytedeskPlugin plugin) {
|
||||
Map<String, Object> 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;
|
||||
}
|
||||
}
|
||||
@@ -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<String, BytedeskPlugin> plugins = new ConcurrentHashMap<>();
|
||||
private final List<BytedeskPlugin> pluginList;
|
||||
|
||||
public PluginRegistry(List<BytedeskPlugin> pluginList) {
|
||||
this.pluginList = pluginList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化:自动注册所有插件
|
||||
*/
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
log.info("Initializing Plugin Registry...");
|
||||
|
||||
// 按优先级排序
|
||||
List<BytedeskPlugin> 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<BytedeskPlugin> getPlugin(String pluginId) {
|
||||
return Optional.ofNullable(plugins.get(pluginId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有插件
|
||||
*/
|
||||
public List<BytedeskPlugin> getAllPlugins() {
|
||||
return new ArrayList<>(plugins.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有已启用的插件
|
||||
*/
|
||||
public List<BytedeskPlugin> 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<String, Map<String, Object>> getAllPluginsHealthStatus() {
|
||||
Map<String, Map<String, Object>> 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<String, Map<String, Object>> getAllPluginsStatistics() {
|
||||
Map<String, Map<String, Object>> 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<BytedeskPlugin> 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");
|
||||
}
|
||||
}
|
||||
353
modules/core/src/main/java/com/bytedesk/core/plugin/README.md
Normal file
353
modules/core/src/main/java/com/bytedesk/core/plugin/README.md
Normal file
@@ -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<BytedeskPlugin> plugins = pluginRegistry.getAllPlugins();
|
||||
|
||||
// 获取特定插件
|
||||
Optional<BytedeskPlugin> plugin = pluginRegistry.getPlugin("kbase");
|
||||
|
||||
// 检查插件是否启用
|
||||
boolean isEnabled = pluginRegistry.isPluginEnabled("service");
|
||||
|
||||
// 获取插件健康状态
|
||||
Map<String, Object> 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
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user