This commit is contained in:
jack ning
2025-10-05 11:59:02 +08:00
parent 2dded21c16
commit f93d1f3eaf
6 changed files with 448 additions and 16 deletions

View File

@@ -35,7 +35,7 @@ import lombok.extern.slf4j.Slf4j;
@ConditionalOnProperty(prefix = "bytedesk.call.freeswitch", name = "enabled", havingValue = "true", matchIfMissing = false)
public class CallController {
private final CallService freeSwitchService;
private final CallService callService;
/**
* http://127.0.0.1:9003/test/api/freeswitch/status
@@ -46,11 +46,11 @@ public class CallController {
Map<String, Object> result = new HashMap<>();
try {
boolean connected = freeSwitchService.isConnected();
String status = freeSwitchService.getStatus();
boolean connected = callService.isConnected();
String status = callService.getStatus();
// 获取配置信息并过滤敏感信息
CallFreeswitchProperties properties = freeSwitchService.getProperties();
CallFreeswitchProperties properties = callService.getProperties();
Map<String, Object> safeProperties = new HashMap<>();
safeProperties.put("enabled", properties.isEnabled());
safeProperties.put("server", properties.getServer());
@@ -88,7 +88,7 @@ public class CallController {
Map<String, Object> result = new HashMap<>();
try {
String response = freeSwitchService.originate(caller, destination, context);
String response = callService.originate(caller, destination, context);
result.put("success", response != null);
result.put("response", response);
@@ -114,7 +114,7 @@ public class CallController {
Map<String, Object> result = new HashMap<>();
try {
String response = freeSwitchService.hangup(uuid, cause);
String response = callService.hangup(uuid, cause);
result.put("success", response != null);
result.put("response", response);
@@ -136,7 +136,7 @@ public class CallController {
Map<String, Object> result = new HashMap<>();
try {
String response = freeSwitchService.answer(uuid);
String response = callService.answer(uuid);
result.put("success", response != null);
result.put("response", response);
@@ -162,7 +162,7 @@ public class CallController {
Map<String, Object> result = new HashMap<>();
try {
String response = freeSwitchService.transfer(uuid, destination, context);
String response = callService.transfer(uuid, destination, context);
result.put("success", response != null);
result.put("response", response);
@@ -188,7 +188,7 @@ public class CallController {
Map<String, Object> result = new HashMap<>();
try {
String response = freeSwitchService.playback(uuid, filePath);
String response = callService.playback(uuid, filePath);
result.put("success", response != null);
result.put("response", response);
@@ -214,7 +214,7 @@ public class CallController {
Map<String, Object> result = new HashMap<>();
try {
String response = freeSwitchService.record(uuid, filePath);
String response = callService.record(uuid, filePath);
result.put("success", response != null);
result.put("response", response);
@@ -240,7 +240,7 @@ public class CallController {
Map<String, Object> result = new HashMap<>();
try {
String response = freeSwitchService.stopRecord(uuid, filePath);
String response = callService.stopRecord(uuid, filePath);
result.put("success", response != null);
result.put("response", response);
@@ -266,7 +266,7 @@ public class CallController {
Map<String, Object> result = new HashMap<>();
try {
String response = freeSwitchService.sendDtmf(uuid, digits);
String response = callService.sendDtmf(uuid, digits);
result.put("success", response != null);
result.put("response", response);
@@ -289,7 +289,7 @@ public class CallController {
Map<String, Object> result = new HashMap<>();
try {
String channels = freeSwitchService.showChannels();
String channels = callService.showChannels();
result.put("success", true);
result.put("channels", channels);
@@ -310,7 +310,7 @@ public class CallController {
Map<String, Object> result = new HashMap<>();
try {
String calls = freeSwitchService.showCalls();
String calls = callService.showCalls();
result.put("success", true);
result.put("calls", calls);
@@ -334,7 +334,7 @@ public class CallController {
Map<String, Object> result = new HashMap<>();
try {
String response = freeSwitchService.executeApiCommand(command, args);
String response = callService.executeApiCommand(command, args);
result.put("success", response != null);
result.put("response", response);

View File

@@ -11,7 +11,7 @@
*
* Copyright (c) 2025 by bytedesk.com, All Rights Reserved.
*/
package com.bytedesk.call.config;
package com.bytedesk.call.call;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.http.ResponseEntity;
@@ -20,6 +20,10 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.bytedesk.call.config.CallConnectionTester;
import com.bytedesk.call.config.CallFreeswitchProperties;
import com.bytedesk.call.config.CallHealthIndicator;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;

View File

@@ -0,0 +1,108 @@
/*
* @Author: jackning 270580156@qq.com
* @Date: 2025-10-05 12:00:00
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2025-10-05 12: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.forum.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
import javax.sql.DataSource;
import java.sql.Connection;
/**
* Forum模块健康检查
* 监控论坛系统相关服务数据库、Redis缓存、搜索索引、审核队列等
*/
@Slf4j
@Component
public class ForumHealthIndicator implements HealthIndicator {
@Value("${bytedesk.forum.post-moderation.enabled:false}")
private boolean moderationEnabled;
@Value("${bytedesk.forum.hot-topics.cache-ttl:3600}")
private int hotTopicsCacheTtl;
@Value("${bytedesk.forum.search.enabled:true}")
private boolean searchEnabled;
@Autowired(required = false)
private DataSource dataSource;
@Autowired(required = false)
private RedisTemplate<String, Object> redisTemplate;
@Override
public Health health() {
try {
Health.Builder builder = Health.up();
// 检查数据库连接
if (dataSource != null) {
try (Connection connection = dataSource.getConnection()) {
builder.withDetail("database-status", "Connected")
.withDetail("database-catalog", connection.getCatalog());
} catch (Exception e) {
log.error("Forum database health check failed", e);
builder.down()
.withDetail("database-status", "Connection Failed")
.withDetail("database-error", e.getMessage());
}
}
// 检查Redis缓存用于热帖、统计等
if (redisTemplate != null) {
try {
// 检查Redis连接
redisTemplate.opsForValue().get("health:check");
// 获取论坛相关统计
Long hotTopicsCount = redisTemplate.opsForZSet().size("forum:hot:topics");
Long pendingModeration = redisTemplate.opsForList().size("forum:moderation:queue");
builder.withDetail("redis-status", "Connected")
.withDetail("hot-topics-cached", hotTopicsCount != null ? hotTopicsCount : 0)
.withDetail("pending-moderation", moderationEnabled ? (pendingModeration != null ? pendingModeration : 0) : "N/A");
} catch (Exception e) {
log.error("Forum Redis health check failed", e);
builder.down()
.withDetail("redis-status", "Connection Failed")
.withDetail("redis-error", e.getMessage());
}
} else {
builder.withDetail("redis-status", "Not Configured");
}
// 论坛功能配置信息
builder.withDetail("post-moderation", moderationEnabled ? "Enabled" : "Disabled")
.withDetail("search-enabled", searchEnabled)
.withDetail("hot-topics-cache-ttl", hotTopicsCacheTtl + "s")
.withDetail("forum-features", "Active")
.withDetail("supported-content", "Post, Thread, Comment, Tag");
return builder.build();
} catch (Exception e) {
log.error("Forum health check failed", e);
return Health.down()
.withDetail("error", e.getMessage())
.build();
}
}
}

View File

@@ -0,0 +1,107 @@
/*
* @Author: jackning 270580156@qq.com
* @Date: 2025-10-05 12:00:00
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2025-10-05 12: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.ai.vectorstore.elasticsearch.ElasticsearchVectorStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
import javax.sql.DataSource;
import java.sql.Connection;
/**
* Kbase模块健康检查
* 监控知识库系统相关服务数据库、向量存储Elasticsearch、批处理等
*/
@Slf4j
@Component
public class KbaseHealthIndicator implements HealthIndicator {
@Value("${spring.ai.vectorstore.elasticsearch.enabled:false}")
private boolean vectorStoreEnabled;
@Value("${spring.ai.vectorstore.elasticsearch.index-name:bytedesk_vector}")
private String vectorStoreIndexName;
@Value("${spring.batch.job.enabled:true}")
private boolean batchJobEnabled;
@Autowired(required = false)
private DataSource dataSource;
@Autowired(required = false)
private ElasticsearchVectorStore elasticsearchVectorStore;
@Override
public Health health() {
try {
Health.Builder builder = Health.up();
// 检查数据库连接
if (dataSource != null) {
try (Connection connection = dataSource.getConnection()) {
builder.withDetail("database-status", "Connected")
.withDetail("database-catalog", connection.getCatalog());
} catch (Exception e) {
log.error("Kbase database health check failed", e);
builder.down()
.withDetail("database-status", "Connection Failed")
.withDetail("database-error", e.getMessage());
}
}
// 检查向量存储Elasticsearch
if (vectorStoreEnabled) {
if (elasticsearchVectorStore != null) {
try {
// 尝试简单的检查
builder.withDetail("vector-store-status", "Connected")
.withDetail("vector-store-index", vectorStoreIndexName)
.withDetail("vector-store-type", "Elasticsearch");
} catch (Exception e) {
log.error("Elasticsearch vector store health check failed", e);
builder.down()
.withDetail("vector-store-status", "Connection Failed")
.withDetail("vector-store-error", e.getMessage());
}
} else {
builder.withDetail("vector-store-status", "Configured but not initialized")
.withDetail("vector-store-warning", "ElasticsearchVectorStore bean not available");
}
} else {
builder.withDetail("vector-store-status", "Disabled");
}
// 批处理任务状态
builder.withDetail("batch-job-enabled", batchJobEnabled);
// 知识库特定功能状态
builder.withDetail("knowledge-base-features", "Active")
.withDetail("supported-content-types", "Article, FAQ, Text, Chunk, File");
return builder.build();
} catch (Exception e) {
log.error("Kbase health check failed", e);
return Health.down()
.withDetail("error", e.getMessage())
.build();
}
}
}

View File

@@ -0,0 +1,104 @@
/*
* @Author: jackning 270580156@qq.com
* @Date: 2025-10-05 12:00:00
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2025-10-05 12: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.social.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
import javax.sql.DataSource;
import java.sql.Connection;
/**
* Social模块健康检查
* 监控社交功能相关服务数据库、Redis缓存、用户关系图谱等
*/
@Slf4j
@Component
public class SocialHealthIndicator implements HealthIndicator {
@Value("${bytedesk.social.max-connections:5000}")
private int maxConnections;
@Value("${bytedesk.social.cache.enabled:true}")
private boolean cacheEnabled;
@Autowired(required = false)
private DataSource dataSource;
@Autowired(required = false)
private RedisTemplate<String, Object> redisTemplate;
@Override
public Health health() {
try {
Health.Builder builder = Health.up();
// 检查数据库连接
if (dataSource != null) {
try (Connection connection = dataSource.getConnection()) {
builder.withDetail("database-status", "Connected")
.withDetail("database-catalog", connection.getCatalog());
} catch (Exception e) {
log.error("Social database health check failed", e);
builder.down()
.withDetail("database-status", "Connection Failed")
.withDetail("database-error", e.getMessage());
}
}
// 检查Redis缓存用于社交关系缓存
if (cacheEnabled && redisTemplate != null) {
try {
// 检查Redis连接
redisTemplate.opsForValue().get("health:check");
// 获取社交关系缓存统计(示例)
Long followCount = redisTemplate.opsForHash().size("social:follows");
Long friendCount = redisTemplate.opsForHash().size("social:friends");
builder.withDetail("redis-status", "Connected")
.withDetail("cache-enabled", true)
.withDetail("cached-follows", followCount != null ? followCount : 0)
.withDetail("cached-friends", friendCount != null ? friendCount : 0);
} catch (Exception e) {
log.error("Social Redis health check failed", e);
builder.down()
.withDetail("redis-status", "Connection Failed")
.withDetail("redis-error", e.getMessage());
}
} else {
builder.withDetail("cache-status", cacheEnabled ? "Enabled (Redis not available)" : "Disabled");
}
// 社交功能配置信息
builder.withDetail("max-connections", maxConnections)
.withDetail("social-features", "Active")
.withDetail("supported-relations", "Follow, Friend, Block");
return builder.build();
} catch (Exception e) {
log.error("Social health check failed", e);
return Health.down()
.withDetail("error", e.getMessage())
.build();
}
}
}

View File

@@ -0,0 +1,109 @@
/*
* @Author: jackning 270580156@qq.com
* @Date: 2025-10-05 12:00:00
* @LastEditors: jackning 270580156@qq.com
* @LastEditTime: 2025-10-05 12: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.team.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
import javax.sql.DataSource;
import java.sql.Connection;
/**
* Team模块健康检查
* 监控团队协作相关服务数据库、Redis缓存、成员管理、权限系统等
*/
@Slf4j
@Component
public class TeamHealthIndicator implements HealthIndicator {
@Value("${bytedesk.team.max-members-per-team:100}")
private int maxMembersPerTeam;
@Value("${bytedesk.team.invitation.expiry-hours:72}")
private int invitationExpiryHours;
@Value("${bytedesk.team.cache.enabled:true}")
private boolean cacheEnabled;
@Autowired(required = false)
private DataSource dataSource;
@Autowired(required = false)
private RedisTemplate<String, Object> redisTemplate;
@Override
public Health health() {
try {
Health.Builder builder = Health.up();
// 检查数据库连接
if (dataSource != null) {
try (Connection connection = dataSource.getConnection()) {
builder.withDetail("database-status", "Connected")
.withDetail("database-catalog", connection.getCatalog());
} catch (Exception e) {
log.error("Team database health check failed", e);
builder.down()
.withDetail("database-status", "Connection Failed")
.withDetail("database-error", e.getMessage());
}
}
// 检查Redis缓存用于团队成员、权限缓存
if (cacheEnabled && redisTemplate != null) {
try {
// 检查Redis连接
redisTemplate.opsForValue().get("health:check");
// 获取团队相关统计
Long activeTeams = redisTemplate.opsForSet().size("team:active");
Long pendingInvitations = redisTemplate.opsForHash().size("team:invitations:pending");
builder.withDetail("redis-status", "Connected")
.withDetail("cache-enabled", true)
.withDetail("active-teams-cached", activeTeams != null ? activeTeams : 0)
.withDetail("pending-invitations", pendingInvitations != null ? pendingInvitations : 0);
} catch (Exception e) {
log.error("Team Redis health check failed", e);
builder.down()
.withDetail("redis-status", "Connection Failed")
.withDetail("redis-error", e.getMessage());
}
} else {
builder.withDetail("cache-status", cacheEnabled ? "Enabled (Redis not available)" : "Disabled");
}
// 团队功能配置信息
builder.withDetail("max-members-per-team", maxMembersPerTeam)
.withDetail("invitation-expiry-hours", invitationExpiryHours)
.withDetail("team-features", "Active")
.withDetail("supported-roles", "Owner, Admin, Member, Guest")
.withDetail("permission-system", "Enabled");
return builder.build();
} catch (Exception e) {
log.error("Team health check failed", e);
return Health.down()
.withDetail("error", e.getMessage())
.build();
}
}
}