初始提交
This commit is contained in:
99
src/main/java/top/gtb520/java/pve_back_api/Main.java
Normal file
99
src/main/java/top/gtb520/java/pve_back_api/Main.java
Normal file
@@ -0,0 +1,99 @@
|
||||
package top.gtb520.java.pve_back_api;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import top.gtb520.java.pve_back_api.config.AppConfig;
|
||||
import top.gtb520.java.pve_back_api.config.ConfigManager;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
@SpringBootApplication
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("=== PVE后端API服务启动 ===");
|
||||
|
||||
// 初始化配置系统
|
||||
initializeConfiguration();
|
||||
|
||||
// 执行初始化
|
||||
Main main = new Main();
|
||||
main.postInitialization();
|
||||
|
||||
// 启动Spring Boot应用
|
||||
SpringApplication app = new SpringApplication(Main.class);
|
||||
app.setDefaultProperties(Collections.singletonMap("server.port", AppConfig.HTTP_PORT));
|
||||
app.run(args);
|
||||
System.out.println("Spring框架加载完成!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化配置系统
|
||||
*/
|
||||
private static void initializeConfiguration() {
|
||||
try {
|
||||
String configPath = ConfigManager.initializeConfig();
|
||||
if (configPath != null) {
|
||||
System.out.println("配置系统初始化成功: " + configPath);
|
||||
|
||||
// 验证配置完整性
|
||||
if (!AppConfig.validateRequiredConfig()) {
|
||||
System.err.println("警告: 必要配置项不完整,请检查配置文件");
|
||||
}
|
||||
} else {
|
||||
System.err.println("配置系统初始化失败!");
|
||||
// 使用默认配置继续运行
|
||||
AppConfig.markAsLoaded();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("配置初始化异常: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
// 即使配置失败也继续启动应用
|
||||
AppConfig.markAsLoaded();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用启动后的初始化工作
|
||||
*/
|
||||
public void postInitialization() {
|
||||
System.out.println("=== 应用初始化 ===");
|
||||
|
||||
if (AppConfig.isConfigLoaded()) {
|
||||
System.out.println("配置状态: 已加载");
|
||||
|
||||
// 显示关键配置信息
|
||||
System.out.println("服务地址: " + AppConfig.HTTP_IP + ":" + AppConfig.HTTP_PORT);
|
||||
System.out.println("PVE地址: " + AppConfig.PVE_URL);
|
||||
|
||||
// 可以在这里添加其他初始化逻辑
|
||||
performAdditionalInitialization();
|
||||
} else {
|
||||
System.out.println("配置状态: 未加载,使用默认配置");
|
||||
}
|
||||
|
||||
System.out.println("应用初始化完成!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行额外的初始化任务
|
||||
*/
|
||||
private void performAdditionalInitialization() {
|
||||
// 这里可以添加数据库连接测试、PVE连接验证等
|
||||
try {
|
||||
// 示例:测试数据库连接配置
|
||||
if (AppConfig.MYSQL_JDBC != null && !AppConfig.MYSQL_JDBC.isEmpty()) {
|
||||
System.out.println("数据库配置已设置");
|
||||
}
|
||||
|
||||
// 示例:测试PVE配置
|
||||
if (AppConfig.PVE_URL != null && !AppConfig.PVE_URL.isEmpty()) {
|
||||
System.out.println("PVE配置已设置");
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("初始化检查时发生错误: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
113
src/main/java/top/gtb520/java/pve_back_api/config/AppConfig.java
Normal file
113
src/main/java/top/gtb520/java/pve_back_api/config/AppConfig.java
Normal file
@@ -0,0 +1,113 @@
|
||||
package top.gtb520.java.pve_back_api.config;
|
||||
|
||||
/**
|
||||
* 全局配置管理类
|
||||
* 将配置文件中的配置项存储到全局静态变量中
|
||||
*/
|
||||
public class AppConfig {
|
||||
|
||||
// 全局配置
|
||||
public static volatile String HTTP_IP = "0.0.0.0";
|
||||
public static volatile String HTTP_PORT = "8080";
|
||||
|
||||
// 数据库配置
|
||||
public static volatile String MYSQL_JDBC = "";
|
||||
public static volatile String MYSQL_USERNAME = "";
|
||||
public static volatile String MYSQL_PASSWORD = "";
|
||||
|
||||
// PVE配置 - 保留原有URL并新增拆解配置
|
||||
public static volatile String PVE_URL = "";
|
||||
public static volatile String PVE_HOSTNAME_IP = "";
|
||||
public static volatile String PVE_PORT = "8006";
|
||||
public static volatile String PVE_PROTOCOL = "https";
|
||||
public static volatile String PVE_API_TOKEN_NAME = "";
|
||||
public static volatile String PVE_API_TOKEN_ID = "";
|
||||
public static volatile String PVE_API_TOKEN_SECRET = "";
|
||||
|
||||
// 配置状态
|
||||
private static volatile boolean isLoaded = false;
|
||||
|
||||
private AppConfig() {
|
||||
// 私有构造函数,防止实例化
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记配置已加载
|
||||
*/
|
||||
public static void markAsLoaded() {
|
||||
isLoaded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查配置是否已加载
|
||||
*/
|
||||
public static boolean isConfigLoaded() {
|
||||
return isLoaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取完整的PVE URL(优先使用拆解后的配置构建)
|
||||
*/
|
||||
public static String getPveFullUrl() {
|
||||
// 如果有拆解后的配置,优先使用它们构建URL
|
||||
if (PVE_HOSTNAME_IP != null && !PVE_HOSTNAME_IP.isEmpty()) {
|
||||
return PVE_PROTOCOL + "://" + PVE_HOSTNAME_IP + ":" + PVE_PORT + "/";
|
||||
}
|
||||
// 否则返回原始URL
|
||||
return PVE_URL != null ? PVE_URL : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取完整的数据库连接URL(包含用户名和密码)
|
||||
*/
|
||||
public static String getFullJdbcUrl() {
|
||||
if (MYSQL_JDBC == null || MYSQL_JDBC.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
return MYSQL_JDBC + "?user=" + MYSQL_USERNAME + "&password=" + MYSQL_PASSWORD;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证必要配置是否完整
|
||||
*/
|
||||
public static boolean validateRequiredConfig() {
|
||||
return HTTP_IP != null && !HTTP_IP.isEmpty() &&
|
||||
HTTP_PORT != null && !HTTP_PORT.isEmpty() &&
|
||||
(PVE_URL != null && !PVE_URL.isEmpty()) ||
|
||||
(PVE_HOSTNAME_IP != null && !PVE_HOSTNAME_IP.isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印当前配置信息
|
||||
*/
|
||||
public static void printCurrentConfig() {
|
||||
System.out.println("=== 应用配置信息 ===");
|
||||
System.out.println("HTTP 监听地址: " + HTTP_IP + ":" + HTTP_PORT);
|
||||
System.out.println("MySQL 连接: " + (MYSQL_JDBC != null ? "已配置" : "未配置"));
|
||||
System.out.println("PVE 原始URL: " + (PVE_URL != null ? PVE_URL : "未配置"));
|
||||
System.out.println("PVE 主机: " + (PVE_HOSTNAME_IP != null ? PVE_HOSTNAME_IP : "未配置"));
|
||||
System.out.println("PVE 端口: " + PVE_PORT);
|
||||
System.out.println("PVE 协议: " + PVE_PROTOCOL);
|
||||
System.out.println("配置加载状态: " + (isLoaded ? "已完成" : "未完成"));
|
||||
System.out.println("==================");
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置配置到默认值
|
||||
*/
|
||||
public static void resetToDefaults() {
|
||||
HTTP_IP = "0.0.0.0";
|
||||
HTTP_PORT = "8080";
|
||||
MYSQL_JDBC = "";
|
||||
MYSQL_USERNAME = "";
|
||||
MYSQL_PASSWORD = "";
|
||||
PVE_URL = "";
|
||||
PVE_HOSTNAME_IP = "";
|
||||
PVE_PORT = "8006";
|
||||
PVE_PROTOCOL = "https";
|
||||
PVE_API_TOKEN_NAME = "";
|
||||
PVE_API_TOKEN_ID = "";
|
||||
PVE_API_TOKEN_SECRET = "";
|
||||
isLoaded = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,405 @@
|
||||
package top.gtb520.java.pve_back_api.config;
|
||||
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
/**
|
||||
* 配置文件管理器
|
||||
* 负责配置文件的检测、创建、加载和解析
|
||||
*/
|
||||
public class ConfigManager {
|
||||
|
||||
private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
private static final String CONFIG_TEMPLATE_PATH = "config.yaml";
|
||||
private static final String CONFIG_DIR_NAME = "conf";
|
||||
private static final String CONFIG_FILE_NAME = "config.yaml";
|
||||
|
||||
/**
|
||||
* 初始化配置系统
|
||||
* @return 配置文件路径
|
||||
*/
|
||||
public static String initializeConfig() {
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
String jarDir = getApplicationDirectory();
|
||||
Path configDir = Paths.get(jarDir, CONFIG_DIR_NAME);
|
||||
Path configFile = configDir.resolve(CONFIG_FILE_NAME);
|
||||
|
||||
System.out.println("应用运行目录: " + jarDir);
|
||||
|
||||
// 确保配置目录存在
|
||||
createConfigDirectory(configDir);
|
||||
|
||||
// 检查并创建配置文件
|
||||
if (!Files.exists(configFile)) {
|
||||
createDefaultConfigFile(configFile);
|
||||
} else {
|
||||
System.out.println("配置文件已存在: " + configFile.toString());
|
||||
}
|
||||
|
||||
// 加载配置到全局变量
|
||||
loadConfiguration(configFile.toString());
|
||||
|
||||
return configFile.toString();
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("配置初始化失败: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取应用运行目录
|
||||
*/
|
||||
private static String getApplicationDirectory() {
|
||||
try {
|
||||
String classPath = ConfigManager.class.getProtectionDomain()
|
||||
.getCodeSource().getLocation().getPath();
|
||||
classPath = java.net.URLDecoder.decode(classPath, "UTF-8");
|
||||
|
||||
File classFile = new File(classPath);
|
||||
|
||||
if (classFile.isFile() && classPath.endsWith(".jar")) {
|
||||
return classFile.getParent();
|
||||
} else if (classFile.isDirectory()) {
|
||||
String projectDir = classFile.getAbsolutePath();
|
||||
if (projectDir.contains("target" + File.separator + "classes")) {
|
||||
return projectDir.substring(0, projectDir.indexOf("target"));
|
||||
}
|
||||
return projectDir;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("获取应用目录失败: " + e.getMessage());
|
||||
}
|
||||
return System.getProperty("user.dir");
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建配置目录
|
||||
*/
|
||||
private static void createConfigDirectory(Path configDir) throws IOException {
|
||||
if (!Files.exists(configDir)) {
|
||||
Files.createDirectories(configDir);
|
||||
System.out.println("创建配置目录: " + configDir.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建默认配置文件
|
||||
*/
|
||||
private static void createDefaultConfigFile(Path configFile) throws IOException {
|
||||
System.out.println("创建默认配置文件: " + configFile.toString());
|
||||
|
||||
try (InputStream templateStream = getResourceAsStream(CONFIG_TEMPLATE_PATH);
|
||||
OutputStream outputStream = Files.newOutputStream(configFile)) {
|
||||
|
||||
if (templateStream == null) {
|
||||
createMinimalConfigFile(configFile);
|
||||
} else {
|
||||
byte[] buffer = new byte[1024];
|
||||
int length;
|
||||
while ((length = templateStream.read(buffer)) > 0) {
|
||||
outputStream.write(buffer, 0, length);
|
||||
}
|
||||
System.out.println("从模板创建配置文件成功");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建最小化配置文件(当模板不可用时)
|
||||
*/
|
||||
private static void createMinimalConfigFile(Path configFile) throws IOException {
|
||||
String minimalConfig = "# PVE后端API配置文件\n" +
|
||||
"global:\n" +
|
||||
" http_ip: \"0.0.0.0\"\n" +
|
||||
" http_port: \"8080\"\n\n" +
|
||||
"database:\n" +
|
||||
" mysql_jdbc: \"\"\n" +
|
||||
" mysql_username: \"\"\n" +
|
||||
" mysql_password: \"\"\n\n" +
|
||||
"pve:\n" +
|
||||
" # 推荐使用拆分配置(优先级更高)\n" +
|
||||
" hostname_ip: \"10.168.2.18\"\n" +
|
||||
" port: \"8006\"\n" +
|
||||
" protocol: \"https\"\n" +
|
||||
" # 或者使用完整URL(向后兼容)\n" +
|
||||
" url: \"https://10.168.2.18:8006/\"\n" +
|
||||
" # API Token配置\n" +
|
||||
" api_tocken_name: \"dev\"\n" +
|
||||
" api_token_id: \"root@pam!dev\"\n" +
|
||||
" api_token_secret: \"68e9dac5-3110-4f1b-b33a-feeaa4014f63\"\n";
|
||||
|
||||
Files.write(configFile, minimalConfig.getBytes("UTF-8"));
|
||||
System.out.println("创建最小化配置文件成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 从资源获取输入流
|
||||
*/
|
||||
private static InputStream getResourceAsStream(String resourcePath) {
|
||||
// 首先尝试从类路径获取
|
||||
InputStream stream = ConfigManager.class.getClassLoader().getResourceAsStream(resourcePath);
|
||||
if (stream != null) {
|
||||
return stream;
|
||||
}
|
||||
|
||||
// 如果类路径中没有,尝试从文件系统获取
|
||||
try {
|
||||
Path resourceFile = Paths.get(resourcePath);
|
||||
if (Files.exists(resourceFile)) {
|
||||
return Files.newInputStream(resourceFile);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// 忽略文件系统访问异常
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载配置文件到全局变量
|
||||
* @param configFilePath 配置文件路径
|
||||
*/
|
||||
private static void loadConfiguration(String configFilePath) {
|
||||
try {
|
||||
Map<String, Object> configData = loadYamlConfig(configFilePath);
|
||||
|
||||
if (configData == null) {
|
||||
handleEmptyConfig();
|
||||
return;
|
||||
}
|
||||
|
||||
// 解析并设置配置值
|
||||
parseAndSetConfig(configData);
|
||||
AppConfig.markAsLoaded();
|
||||
|
||||
System.out.println("✅ 配置加载成功!");
|
||||
AppConfig.printCurrentConfig();
|
||||
|
||||
} catch (Exception e) {
|
||||
handleConfigLoadError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载YAML配置文件
|
||||
*/
|
||||
private static Map<String, Object> loadYamlConfig(String configFilePath) throws IOException {
|
||||
Yaml yaml = new Yaml();
|
||||
try (InputStream inputStream = Files.newInputStream(Paths.get(configFilePath))) {
|
||||
return yaml.load(inputStream);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理空配置情况
|
||||
*/
|
||||
private static void handleEmptyConfig() {
|
||||
System.err.println("❌ 配置文件为空或格式错误");
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理配置加载错误
|
||||
*/
|
||||
private static void handleConfigLoadError(Exception e) {
|
||||
System.err.println("❌ 加载配置文件失败: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析配置数据并设置到全局变量
|
||||
* @param data 配置数据映射
|
||||
*/
|
||||
private static void parseAndSetConfig(Map<String, Object> data) {
|
||||
try {
|
||||
parseGlobalSection(data);
|
||||
parseDatabaseSection(data);
|
||||
parsePveSection(data);
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("❌ 解析配置数据时发生错误: " + e.getMessage());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析全局配置部分
|
||||
*/
|
||||
private static void parseGlobalSection(Map<String, Object> data) {
|
||||
if (!data.containsKey("global")) {
|
||||
return;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> global = (Map<String, Object>) data.get("global");
|
||||
AppConfig.HTTP_IP = getStringValue(global, "http_ip", AppConfig.HTTP_IP);
|
||||
AppConfig.HTTP_PORT = getStringValue(global, "http_port", AppConfig.HTTP_PORT);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析数据库配置部分
|
||||
*/
|
||||
private static void parseDatabaseSection(Map<String, Object> data) {
|
||||
if (!data.containsKey("database")) {
|
||||
return;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> database = (Map<String, Object>) data.get("database");
|
||||
AppConfig.MYSQL_JDBC = getStringValue(database, "mysql_jdbc", AppConfig.MYSQL_JDBC);
|
||||
AppConfig.MYSQL_USERNAME = getStringValue(database, "mysql_username", AppConfig.MYSQL_USERNAME);
|
||||
AppConfig.MYSQL_PASSWORD = getStringValue(database, "mysql_password", AppConfig.MYSQL_PASSWORD);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析PVE配置部分
|
||||
*/
|
||||
private static void parsePveSection(Map<String, Object> data) {
|
||||
if (!data.containsKey("pve")) {
|
||||
return;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> pve = (Map<String, Object>) data.get("pve");
|
||||
processPveUrlConfig(pve);
|
||||
processPveApiTokens(pve);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理PVE URL相关配置
|
||||
*/
|
||||
private static void processPveUrlConfig(Map<String, Object> pve) {
|
||||
// 保留原始URL配置
|
||||
AppConfig.PVE_URL = getStringValue(pve, "url", AppConfig.PVE_URL);
|
||||
|
||||
// 解析拆分后的PVE配置(优先级更高)
|
||||
AppConfig.PVE_HOSTNAME_IP = getStringValue(pve, "hostname_ip", AppConfig.PVE_HOSTNAME_IP);
|
||||
AppConfig.PVE_PORT = getStringValue(pve, "port", AppConfig.PVE_PORT);
|
||||
AppConfig.PVE_PROTOCOL = getStringValue(pve, "protocol", AppConfig.PVE_PROTOCOL);
|
||||
|
||||
// 如果提供了拆分配置,同时更新URL
|
||||
if (hasValidHostname()) {
|
||||
AppConfig.PVE_URL = buildPveUrl();
|
||||
}
|
||||
// 如果只有URL而没有拆分配置,则解析URL
|
||||
else if (hasValidUrl()) {
|
||||
parsePveUrl(AppConfig.PVE_URL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理PVE API令牌配置
|
||||
*/
|
||||
private static void processPveApiTokens(Map<String, Object> pve) {
|
||||
AppConfig.PVE_API_TOKEN_NAME = getStringValue(pve, "api_tocken_name", AppConfig.PVE_API_TOKEN_NAME);
|
||||
AppConfig.PVE_API_TOKEN_ID = getStringValue(pve, "api_token_id", AppConfig.PVE_API_TOKEN_ID);
|
||||
AppConfig.PVE_API_TOKEN_SECRET = getStringValue(pve, "api_token_secret", AppConfig.PVE_API_TOKEN_SECRET);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否有有效的主机名配置
|
||||
*/
|
||||
private static boolean hasValidHostname() {
|
||||
return AppConfig.PVE_HOSTNAME_IP != null && !AppConfig.PVE_HOSTNAME_IP.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否有有效的URL配置
|
||||
*/
|
||||
private static boolean hasValidUrl() {
|
||||
return AppConfig.PVE_URL != null && !AppConfig.PVE_URL.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建PVE完整URL
|
||||
*/
|
||||
private static String buildPveUrl() {
|
||||
return AppConfig.PVE_PROTOCOL + "://" + AppConfig.PVE_HOSTNAME_IP + ":" + AppConfig.PVE_PORT + "/";
|
||||
}
|
||||
|
||||
/**
|
||||
* 从完整URL解析PVE主机和端口信息
|
||||
*/
|
||||
private static void parsePveUrl(String url) {
|
||||
try {
|
||||
if (url == null || url.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 移除末尾的斜杠
|
||||
if (url.endsWith("/")) {
|
||||
url = url.substring(0, url.length() - 1);
|
||||
}
|
||||
|
||||
// 解析协议
|
||||
if (url.startsWith("https://")) {
|
||||
AppConfig.PVE_PROTOCOL = "https";
|
||||
url = url.substring(8);
|
||||
} else if (url.startsWith("http://")) {
|
||||
AppConfig.PVE_PROTOCOL = "http";
|
||||
url = url.substring(7);
|
||||
}
|
||||
|
||||
// 解析主机和端口
|
||||
int portIndex = url.lastIndexOf(":");
|
||||
if (portIndex > 0) {
|
||||
AppConfig.PVE_HOSTNAME_IP = url.substring(0, portIndex);
|
||||
String portStr = url.substring(portIndex + 1);
|
||||
if (!portStr.isEmpty()) {
|
||||
AppConfig.PVE_PORT = portStr;
|
||||
}
|
||||
} else {
|
||||
AppConfig.PVE_HOSTNAME_IP = url;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("解析PVE URL时发生错误: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全获取字符串值
|
||||
*/
|
||||
private static String getStringValue(Map<String, Object> map, String key, String defaultValue) {
|
||||
if (map.containsKey(key)) {
|
||||
Object value = map.get(key);
|
||||
return value != null ? value.toString() : defaultValue;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新加载配置
|
||||
*/
|
||||
public static boolean reloadConfiguration() {
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
String jarDir = getApplicationDirectory();
|
||||
Path configFile = Paths.get(jarDir, CONFIG_DIR_NAME, CONFIG_FILE_NAME);
|
||||
|
||||
if (Files.exists(configFile)) {
|
||||
AppConfig.resetToDefaults();
|
||||
loadConfiguration(configFile.toString());
|
||||
return true;
|
||||
} else {
|
||||
System.err.println("配置文件不存在,无法重新加载");
|
||||
return false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("重新加载配置失败: " + e.getMessage());
|
||||
return false;
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package top.gtb520.java.pve_back_api.config;
|
||||
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import java.io.InputStream;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 全局配置类
|
||||
* 存储所有配置项的全局变量
|
||||
*/
|
||||
public class GlobalConfig {
|
||||
|
||||
// 全局配置
|
||||
public static String HTTP_IP = "0.0.0.0";
|
||||
public static String HTTP_PORT = "8080";
|
||||
|
||||
// 数据库配置
|
||||
public static String MYSQL_JDBC = "";
|
||||
public static String MYSQL_USERNAME = "";
|
||||
public static String MYSQL_PASSWORD = "";
|
||||
|
||||
// PVE配置
|
||||
public static String PVE_URL = "";
|
||||
public static String PVE_API_TOKEN_NAME = "";
|
||||
public static String PVE_API_TOKEN_ID = "";
|
||||
public static String PVE_API_TOKEN_SECRET = "";
|
||||
|
||||
// 私有构造函数,防止实例化
|
||||
private GlobalConfig() {}
|
||||
|
||||
/**
|
||||
* 打印所有配置信息(用于调试)
|
||||
*/
|
||||
public static void printConfig() {
|
||||
System.out.println("=== 当前配置信息 ===");
|
||||
System.out.println("HTTP IP: " + HTTP_IP);
|
||||
System.out.println("HTTP PORT: " + HTTP_PORT);
|
||||
System.out.println("MYSQL JDBC: " + MYSQL_JDBC);
|
||||
System.out.println("MYSQL USERNAME: " + MYSQL_USERNAME);
|
||||
System.out.println("PVE URL: " + PVE_URL);
|
||||
System.out.println("PVE API TOKEN NAME: " + PVE_API_TOKEN_NAME);
|
||||
System.out.println("PVE API TOKEN ID: " + PVE_API_TOKEN_ID);
|
||||
System.out.println("===================");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
package top.gtb520.java.pve_back_api.config;
|
||||
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* YAML配置文件解析器
|
||||
* 负责从不同来源加载和解析YAML配置文件
|
||||
*/
|
||||
public class YamlConfigLoader {
|
||||
|
||||
private static final String GLOBAL_SECTION = "global";
|
||||
private static final String DATABASE_SECTION = "database";
|
||||
private static final String PVE_SECTION = "pve";
|
||||
|
||||
/**
|
||||
* 从资源文件加载配置
|
||||
* @param resourcePath 资源文件路径
|
||||
*/
|
||||
public static void loadConfigFromResource(String resourcePath) {
|
||||
try {
|
||||
Yaml yaml = new Yaml();
|
||||
InputStream inputStream = YamlConfigLoader.class.getClassLoader().getResourceAsStream(resourcePath);
|
||||
|
||||
if (inputStream == null) {
|
||||
handleResourceNotFound(resourcePath);
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Object> data = yaml.load(inputStream);
|
||||
parseConfig(data);
|
||||
|
||||
} catch (Exception e) {
|
||||
handleLoadException("资源", resourcePath, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从文件路径加载配置
|
||||
* @param filePath 文件路径
|
||||
*/
|
||||
public static void loadConfigFromFile(String filePath) {
|
||||
try {
|
||||
Yaml yaml = new Yaml();
|
||||
InputStream inputStream = java.nio.file.Files.newInputStream(java.nio.file.Paths.get(filePath));
|
||||
|
||||
Map<String, Object> data = yaml.load(inputStream);
|
||||
parseConfig(data);
|
||||
|
||||
} catch (Exception e) {
|
||||
handleLoadException("文件", filePath, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析配置数据并设置到全局变量
|
||||
* @param data 配置数据映射
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static void parseConfig(Map<String, Object> data) {
|
||||
try {
|
||||
parseGlobalConfig(data);
|
||||
parseDatabaseConfig(data);
|
||||
parsePveConfig(data);
|
||||
|
||||
System.out.println("配置加载成功!");
|
||||
GlobalConfig.printConfig();
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("解析配置数据时发生错误: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析全局配置部分
|
||||
*/
|
||||
private static void parseGlobalConfig(Map<String, Object> data) {
|
||||
if (!data.containsKey(GLOBAL_SECTION)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> global = (Map<String, Object>) data.get(GLOBAL_SECTION);
|
||||
GlobalConfig.HTTP_IP = getStringValue(global, "http_ip", GlobalConfig.HTTP_IP);
|
||||
GlobalConfig.HTTP_PORT = getStringValue(global, "http_port", GlobalConfig.HTTP_PORT);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析数据库配置部分
|
||||
*/
|
||||
private static void parseDatabaseConfig(Map<String, Object> data) {
|
||||
if (!data.containsKey(DATABASE_SECTION)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> database = (Map<String, Object>) data.get(DATABASE_SECTION);
|
||||
GlobalConfig.MYSQL_JDBC = getStringValue(database, "mysql_jdbc", GlobalConfig.MYSQL_JDBC);
|
||||
GlobalConfig.MYSQL_USERNAME = getStringValue(database, "mysql_username", GlobalConfig.MYSQL_USERNAME);
|
||||
GlobalConfig.MYSQL_PASSWORD = getStringValue(database, "mysql_password", GlobalConfig.MYSQL_PASSWORD);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析PVE配置部分
|
||||
*/
|
||||
private static void parsePveConfig(Map<String, Object> data) {
|
||||
if (!data.containsKey(PVE_SECTION)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> pve = (Map<String, Object>) data.get(PVE_SECTION);
|
||||
GlobalConfig.PVE_URL = getStringValue(pve, "url", GlobalConfig.PVE_URL);
|
||||
GlobalConfig.PVE_API_TOKEN_NAME = getStringValue(pve, "api_tocken_name", GlobalConfig.PVE_API_TOKEN_NAME);
|
||||
GlobalConfig.PVE_API_TOKEN_ID = getStringValue(pve, "api_token_id", GlobalConfig.PVE_API_TOKEN_ID);
|
||||
GlobalConfig.PVE_API_TOKEN_SECRET = getStringValue(pve, "api_token_secret", GlobalConfig.PVE_API_TOKEN_SECRET);
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全获取字符串值
|
||||
*/
|
||||
private static String getStringValue(Map<String, Object> map, String key, String defaultValue) {
|
||||
if (map.containsKey(key)) {
|
||||
Object value = map.get(key);
|
||||
return value != null ? value.toString() : defaultValue;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理资源未找到的情况
|
||||
*/
|
||||
private static void handleResourceNotFound(String resourcePath) {
|
||||
System.err.println("❌ 无法找到资源配置文件: " + resourcePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理加载异常
|
||||
*/
|
||||
private static void handleLoadException(String type, String path, Exception e) {
|
||||
System.err.println("❌ 解析" + type + "配置文件失败: " + path);
|
||||
System.err.println("错误详情: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
168
src/main/java/top/gtb520/java/pve_back_api/route/pve/status.java
Normal file
168
src/main/java/top/gtb520/java/pve_back_api/route/pve/status.java
Normal file
@@ -0,0 +1,168 @@
|
||||
package top.gtb520.java.pve_back_api.route.pve;
|
||||
|
||||
import it.corsinvest.proxmoxve.api.*;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
// 导入配置变量
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static top.gtb520.java.pve_back_api.config.AppConfig.*;
|
||||
|
||||
/**
|
||||
* PVE状态相关路由处理器
|
||||
* 提供与Proxmox VE服务器通信的客户端创建功能和状态查询
|
||||
*/
|
||||
@RestController
|
||||
public class status {
|
||||
|
||||
private static final int DEFAULT_TIMEOUT = 120000; // 2分钟超时时间
|
||||
|
||||
/**
|
||||
* API:/api/pve/status
|
||||
* 类型:GET
|
||||
* 获取PVE集群状态信息,包括节点、虚拟机、容器等资源状态
|
||||
*
|
||||
* @return 包含集群状态信息的列表
|
||||
* @throws Exception 当获取状态失败时抛出
|
||||
*/
|
||||
@GetMapping("/api/pve/status")
|
||||
public List<Map<String, Object>> GetPveStatus() throws Exception {
|
||||
System.out.println("开始获取PVE状态信息...");
|
||||
List<Map<String, Object>> pveStatus = new ArrayList<>();
|
||||
|
||||
try {
|
||||
// 创建PVE客户端
|
||||
PveClient client = createClient();
|
||||
|
||||
// 获取集群资源信息
|
||||
var resourcesResult = client.getCluster().getResources().resources();
|
||||
|
||||
if (resourcesResult.isSuccessStatusCode()) {
|
||||
JsonNode resources = resourcesResult.getResponse().get("data");
|
||||
|
||||
// 统计各类资源
|
||||
int nodeCount = 0, vmCount = 0, ctCount = 0;
|
||||
int runningNodes = 0, runningVms = 0, runningCts = 0;
|
||||
|
||||
// 处理每个资源
|
||||
for (JsonNode resource : resources) {
|
||||
String type = resource.get("type").asText();
|
||||
String status = resource.get("status").asText();
|
||||
|
||||
Map<String, Object> resourceInfo = new HashMap<>();
|
||||
resourceInfo.put("type", type);
|
||||
resourceInfo.put("status", status);
|
||||
|
||||
switch (type) {
|
||||
case "node":
|
||||
nodeCount++;
|
||||
if ("online".equals(status)) runningNodes++;
|
||||
resourceInfo.put("name", resource.get("node").asText());
|
||||
resourceInfo.put("cpu_usage", String.format("%.2f%%",
|
||||
resource.get("cpu").asDouble() * 100));
|
||||
resourceInfo.put("memory_usage", String.format("%.2f%%",
|
||||
(resource.get("mem").asDouble() / resource.get("maxmem").asDouble()) * 100));
|
||||
resourceInfo.put("uptime", formatUptime(resource.get("uptime").asInt()));
|
||||
break;
|
||||
|
||||
case "qemu":
|
||||
vmCount++;
|
||||
if ("running".equals(status)) runningVms++;
|
||||
resourceInfo.put("vmid", resource.get("vmid").asInt());
|
||||
resourceInfo.put("name", resource.get("name").asText());
|
||||
resourceInfo.put("node", resource.get("node").asText());
|
||||
break;
|
||||
|
||||
case "lxc":
|
||||
ctCount++;
|
||||
if ("running".equals(status)) runningCts++;
|
||||
resourceInfo.put("vmid", resource.get("vmid").asInt());
|
||||
resourceInfo.put("name", resource.get("name").asText());
|
||||
resourceInfo.put("node", resource.get("node").asText());
|
||||
break;
|
||||
}
|
||||
|
||||
pveStatus.add(resourceInfo);
|
||||
}
|
||||
|
||||
// 添加统计信息
|
||||
Map<String, Object> summary = new HashMap<>();
|
||||
summary.put("summary", true);
|
||||
summary.put("total_nodes", nodeCount);
|
||||
summary.put("running_nodes", runningNodes);
|
||||
summary.put("total_vms", vmCount);
|
||||
summary.put("running_vms", runningVms);
|
||||
summary.put("total_containers", ctCount);
|
||||
summary.put("running_containers", runningCts);
|
||||
summary.put("timestamp", System.currentTimeMillis());
|
||||
pveStatus.add(summary);
|
||||
|
||||
System.out.println("✅ 成功获取PVE状态信息");
|
||||
System.out.printf("📊 统计: 节点%d(%d运行), VM%d(%d运行), 容器%d(%d运行)%n",
|
||||
nodeCount, runningNodes, vmCount, runningVms, ctCount, runningCts);
|
||||
|
||||
} else {
|
||||
// 更安全的错误处理
|
||||
String errorMsg = "未知错误";
|
||||
try {
|
||||
if (resourcesResult != null) {
|
||||
errorMsg = resourcesResult.getError();
|
||||
} else {
|
||||
errorMsg = "API调用返回null结果";
|
||||
}
|
||||
} catch (Exception e) {
|
||||
errorMsg = "错误处理异常: " + e.getClass().getSimpleName() + " - " + e.getMessage();
|
||||
}
|
||||
throw new Exception("获取集群资源失败: " + errorMsg);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("❌ 获取PVE状态失败: " + e.getMessage());
|
||||
throw new Exception("获取PVE状态失败: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
return pveStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化运行时间
|
||||
*/
|
||||
private static String formatUptime(int seconds) {
|
||||
int days = seconds / 86400;
|
||||
int hours = (seconds % 86400) / 3600;
|
||||
int minutes = (seconds % 3600) / 60;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (days > 0) sb.append(days).append("天 ");
|
||||
if (hours > 0) sb.append(hours).append("小时 ");
|
||||
if (minutes > 0) sb.append(minutes).append("分钟");
|
||||
|
||||
return sb.toString().trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建PVE客户端实例
|
||||
* 支持API令牌认证和传统登录认证两种方式
|
||||
*
|
||||
* @return 配置好的PveClient实例
|
||||
* @throws RuntimeException 当客户端创建失败时抛出
|
||||
*/
|
||||
public static PveClient createClient() throws RuntimeException {
|
||||
// 构建API_TOKEN字符串
|
||||
String[] api_token_list = PVE_API_TOKEN_ID.split("!");
|
||||
String api_token = api_token_list[0] + ":" + api_token_list[1] + "@" + "token=" + PVE_API_TOKEN_SECRET;
|
||||
System.out.println("API_TOKEN: " + api_token);
|
||||
|
||||
// 创建PVE客户端实例
|
||||
var client = new PveClient(PVE_HOSTNAME_IP, Integer.parseInt(PVE_PORT));
|
||||
client.setValidateCertificate(false); // SSL证书验证
|
||||
client.setTimeout(DEFAULT_TIMEOUT); // 设置请求超时时间
|
||||
client.setApiToken(api_token);
|
||||
return client;
|
||||
}
|
||||
}
|
||||
12
src/main/java/top/gtb520/java/pve_back_api/route/test.java
Normal file
12
src/main/java/top/gtb520/java/pve_back_api/route/test.java
Normal file
@@ -0,0 +1,12 @@
|
||||
package top.gtb520.java.pve_back_api.route;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class test {
|
||||
@GetMapping("/test")
|
||||
public String test() {
|
||||
return "test";
|
||||
}
|
||||
}
|
||||
1
src/main/resources/application.properties
Normal file
1
src/main/resources/application.properties
Normal file
@@ -0,0 +1 @@
|
||||
spring.application.name=pve-back-api
|
||||
18
src/main/resources/config.yaml
Normal file
18
src/main/resources/config.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
# 全局配置
|
||||
global:
|
||||
http_ip: "0.0.0.0"
|
||||
http_port: "8080"
|
||||
|
||||
# 数据库配置
|
||||
database:
|
||||
mysql_jdbc: "jdbc:mysql://10.168.2.2:3306/pve_monitor?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&allowMultiQueries=true&useSSL=false&allowPublicKeyRetrieval=true&tinyInt1isBit=false&allowLoadLocalInfile=true&allowLocalInfile=true&allowUrlInLocalInfile=true&allowPublicKeyRetrieval=true&allowMultiQueries=true&allowPublicKeyRetrieval=true&allowLoadLocalInfile=true&allowUrlInLocalInfile=true&allowPublicKeyRetrieval=true&allowMultiQueries=true&allowPublicKeyRetrieval=true&allowLoadLocalInfile=true&allowUrlInLocal"
|
||||
mysql_username: "root"
|
||||
mysql_password: "Password"
|
||||
|
||||
# PVE配置
|
||||
pve:
|
||||
# PVE后端地址,示例:https://192.168.1.1:8006/ (协议HTTPS,带结尾的“/”,不保留结尾其他参数)
|
||||
url: "https://10.168.2.18:8006/"
|
||||
api_tocken_name: "dev"
|
||||
api_token_id: "root@pam!dev"
|
||||
api_token_secret: "68e9dac5-3110-4f1b-b33a-feeaa4014f63"
|
||||
56
src/test/java/top/gtb520/java/pve_back_api/MainTests.java
Normal file
56
src/test/java/top/gtb520/java/pve_back_api/MainTests.java
Normal file
@@ -0,0 +1,56 @@
|
||||
package top.gtb520.java.pve_back_api;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import top.gtb520.java.pve_back_api.config.AppConfig;
|
||||
import top.gtb520.java.pve_back_api.config.ConfigManager;
|
||||
import top.gtb520.java.pve_back_api.config.YamlConfigLoader;
|
||||
import top.gtb520.java.pve_back_api.route.pve.status;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@SpringBootTest
|
||||
class MainTests {
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAppConfigInitialization() {
|
||||
// 测试配置类的基本功能
|
||||
assertNotNull(AppConfig.HTTP_IP);
|
||||
assertNotNull(AppConfig.HTTP_PORT);
|
||||
assertFalse(AppConfig.isConfigLoaded());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConfigManagerMethodsExist() {
|
||||
// 测试ConfigManager类的方法是否存在
|
||||
assertTrue(ConfigManager.class.getDeclaredMethods().length > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testYamlConfigLoaderMethodsExist() {
|
||||
// 测试YamlConfigLoader类的方法是否存在
|
||||
assertTrue(YamlConfigLoader.class.getDeclaredMethods().length > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testStatusClassExists() {
|
||||
// 测试status类是否存在createClient方法
|
||||
assertNotNull(status.class.getDeclaredMethods());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPveUrlBuilding() {
|
||||
// 测试PVE URL构建逻辑
|
||||
AppConfig.PVE_PROTOCOL = "https";
|
||||
AppConfig.PVE_HOSTNAME_IP = "192.168.1.100";
|
||||
AppConfig.PVE_PORT = "8006";
|
||||
|
||||
String expectedUrl = "https://192.168.1.100:8006/";
|
||||
assertEquals(expectedUrl, AppConfig.getPveFullUrl());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package top.gtb520.java.pve_back_api;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import reactor.core.publisher.Mono;
|
||||
import org.springframework.http.client.reactive.ClientHttpConnector;
|
||||
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
|
||||
import reactor.netty.http.client.HttpClient;
|
||||
import reactor.netty.tcp.TcpClient;
|
||||
|
||||
public class ResourcesResultTest {
|
||||
|
||||
/**
|
||||
* 创建忽略SSL证书验证的WebClient实例
|
||||
* 用于测试环境或自签名证书场景
|
||||
*/
|
||||
private static WebClient createInsecureWebClient() {
|
||||
try {
|
||||
// 使用Netty内置的不安全信任管理器工厂
|
||||
TcpClient tcpClient = TcpClient.create()
|
||||
.secure(sslContextSpec -> {
|
||||
try {
|
||||
sslContextSpec.sslContext(
|
||||
io.netty.handler.ssl.SslContextBuilder.forClient()
|
||||
.trustManager(io.netty.handler.ssl.util.InsecureTrustManagerFactory.INSTANCE)
|
||||
.build()
|
||||
);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("SSL配置失败", e);
|
||||
}
|
||||
});
|
||||
|
||||
// 创建HttpClient和连接器
|
||||
HttpClient httpClient = HttpClient.from(tcpClient);
|
||||
ClientHttpConnector connector = new ReactorClientHttpConnector(httpClient);
|
||||
|
||||
// 构建并返回WebClient
|
||||
return WebClient.builder()
|
||||
.clientConnector(connector)
|
||||
.build();
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("创建WebClient失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHttpsRequest() {
|
||||
System.out.println("开始发送HTTPS请求...");
|
||||
|
||||
try {
|
||||
// 创建忽略证书验证的WebClient
|
||||
WebClient client = createInsecureWebClient();
|
||||
|
||||
// 发送HTTPS GET请求
|
||||
Mono<String> result = client.get()
|
||||
.uri("https://10.168.2.2:8013/")
|
||||
.retrieve()
|
||||
.bodyToMono(String.class);
|
||||
|
||||
// 阻塞获取响应结果
|
||||
String response = result.block();
|
||||
System.out.println("✅ 请求成功!");
|
||||
System.out.println("响应内容长度: " + (response != null ? response.length() : 0) + " 字符");
|
||||
|
||||
// 如果需要查看完整响应内容,取消下面的注释
|
||||
// System.out.println("响应内容: " + response);
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("❌ HTTPS请求失败: " + e.getMessage());
|
||||
System.err.println("可能的原因:");
|
||||
System.err.println("1. 目标服务器不可达");
|
||||
System.err.println("2. 端口未开放");
|
||||
System.err.println("3. 网络连接问题");
|
||||
System.err.println("4. SSL配置问题");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user