添加最后标签编译脚本,程序启动与单元测试流程优化。

This commit is contained in:
2026-01-21 16:21:20 +08:00
parent 73dfac7822
commit 014e20b37f
9 changed files with 561 additions and 311 deletions

1
.gitignore vendored
View File

@@ -5,6 +5,7 @@ reports/
target/
rsakeys/
config.ini
version.flags
*.log
// 忽略经常变更的Jar文件

View File

@@ -0,0 +1,44 @@
#!/bin/bash
set -euo pipefail
# 前置校验当前是否为git仓库非git仓库直接退出
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
echo "错误当前目录不是git仓库终止执行"
exit 1
fi
# 重置本地修改、切main分支拉最新代码
git restore .
git checkout main || { echo "切换main分支失败终止执行"; exit 1; }
git pull origin main --rebase || { echo "拉取main分支最新代码失败终止执行"; exit 1; }
# 优先取HEAD关联tag无则取仓库最近tag
get_latest_tag() {
local head_tag=$(git log -1 --pretty=format:%D 2>/dev/null | grep -o 'tag: [^, ]*' | awk -F': ' '{print $2}')
[ -n "$head_tag" ] && { echo "$head_tag"; return 0; }
local latest_tag=$(git describe --tags --abbrev=0 2>/dev/null || true)
[ -n "$latest_tag" ] && { echo "$latest_tag"; return 0; }
echo "no_tag" && return 1
}
# 获取并校验tag无tag直接退出
latest_tag=$(get_latest_tag)
if [ "$latest_tag" = "no_tag" ]; then
echo "仓库无任何tag终止执行"
exit 1
fi
echo "获取到目标tag$latest_tag"
# 切换tag失败退出
git checkout "$latest_tag" || { echo "切换至tag $latest_tag失败,终止执行"; exit 1; }
# 执行构建脚本,失败直接退出
bash bash/build_class.sh || { echo "构建脚本执行失败,终止执行"; exit 1; }
# 确保config目录存在写入tag信息
mkdir -p config
echo "$latest_tag" > config/version.flags
echo "✅ 全部操作完成tag已写入config/version.flags"

View File

@@ -1,81 +1,121 @@
import cc.winboll.LogUtils;
import cc.winboll.WinBoLL;
import cc.winboll.test.ConsoleCmdAutoTest;
import cc.winboll.util.ConsoleInputUtils;
import cc.winboll.util.IniConfigUtils;
import java.util.Date;
import java.util.logging.Level;
/**
* AuthCenter 程序入口类
* 负责程序启动流程、INI配置加载、日志工具初始化及自动化测试执行调度
* 流程规范:加载配置→初始化日志→执行业务→收尾输出,异常兜底保障程序优雅退出
* 核心职责:统一调度程序启动流程,保障启动顺序规范性与异常兜底能力
* 启动标准流程:基础信息输出 → INI配置加载 → 日志工具初始化 → 自动化测试执行 → 业务启动 → 优雅停机
* 适配Java7语法 & Android API30异常场景兜底提示保障程序优雅退出
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Date 2026-01-27 17:00:00
* @LastEditTime 2026-01-27 22:32:18
* @LastEditTime 2026-01-29 14:00:00 优化启动逻辑+规范格式+补全注释+修复流程判断BUG
*/
public class Main {
public static void main(String[] args) {
// 程序启动基础信息输出
// 1. 程序启动基础信息输出
printStartInfo(args);
// 2. 注册JVM停机钩子实现优雅退出
registerShutdownHook();
// 3. 核心启动流程严格顺序INI配置→日志→自动化测试全成功才执行业务
try {
boolean initSuccess = startCoreProcess();
if (!initSuccess) {
System.err.println("启动失败,程序退出");
System.exit(-1);
}
// 4. 执行业务核心逻辑(此处放开注释即可启动主业务)
LogUtils.i("Main", "正在处理核心事务...");
WinBoLL.main(args);
// 5. 程序正常结束信息输出
printEndInfo();
} catch (Exception e) {
LogUtils.e("Main", "程序运行异常,强制退出", e);
System.err.println("程序运行异常:" + e.getMessage());
System.exit(-1);
}
}
/**
* 打印程序启动基础信息,含启动时间+入参
*/
private static void printStartInfo(String[] args) {
System.out.println("==== 程序启动 ====");
System.out.println("启动时间:" + new Date());
System.out.println("程序开始运行...");
System.out.println("【main函数】传入启动参数" + (args == null ? "无参数" : arrayToString(args)));
System.out.println("【main函数】传入启动参数" + arrayToString(args));
}
/**
* 注册JVM停机钩子收到停止信号时执行优雅退出逻辑
*/
private static void registerShutdownHook() {
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
System.out.println("[Main] 收到JVM停止信号执行服务优雅停止流程");
ConsoleInputUtils.handleExitCmd();
}
}, "GracefulStop-Hook"));
}
/**
* 核心启动流程调度严格按顺序执行全环节成功返回true
* 流程INI配置初始化 → 日志工具初始化 → 自动化测试(全用例通过)
* @return 全流程成功返回true任意环节失败返回false
*/
private static boolean startCoreProcess() {
// 步骤1初始化INI配置基础依赖必须最先执行
if (!IniConfigUtils.init()) {
LogUtils.e("Main", "INI配置初始化失败");
return false;
}
LogUtils.i("Main", "INI配置初始化成功");
// 步骤2初始化日志工具依赖INI配置后续日志输出依赖
try {
// 加载INI配置 带调试日志
System.out.println("\n【函数调用】IniConfigUtils.loadConfig(null) 开始加载配置文件");
boolean configLoadFlag = IniConfigUtils.loadConfig(null);
System.out.println("【调用结果】IniConfigUtils.loadConfig 执行结果:" + configLoadFlag);
if (!configLoadFlag) {
System.err.println("INI配置文件加载失败程序无法启动强制退出");
System.exit(1);
}
System.out.println("INI配置文件加载成功");
try {
// 日志工具初始化 调试输出
System.out.println("\n【日志初始化】开始初始化日志工具...");
System.out.println("\n【全开日志测试】...");
// 新增临时全开日志级别让test所有日志显示
Level oldLevel = LogUtils.getGlobalLogLevel();
LogUtils.setGlobalLogLevel(Level.ALL);
LogUtils.test(); // 执行测试
LogUtils.setGlobalLogLevel(oldLevel); // 还原原级别
System.out.println("\n【配置日志测试】...");
// 单元测试
ConsoleCmdAutoTest.main(args);
LogUtils.i("Main", "正在处理事务...");
WinBoLL.main(args);
} finally {
// 收尾日志 必执行
System.out.println("【收尾执行】finally块 执行测试收尾操作");
LogUtils.i("Main", "测试执行完毕");
}
LogUtils.init();
LogUtils.i("Main", "日志工具初始化成功");
} catch (Exception e) {
// 异常捕获 输出详情
System.err.println("\n【程序异常】执行过程出现异常异常信息" + e.getMessage());
e.printStackTrace();
LogUtils.e("Main", "日志工具初始化失败", e);
return false;
}
// 程序结束信息
// 步骤3执行自动化测试全用例通过才放行失败则终止
boolean testPass = ConsoleCmdAutoTest.main(null);
if (!testPass) {
LogUtils.e("Main", "自动化测试存在失败用例,启动终止");
return false;
}
LogUtils.i("Main", "自动化测试全用例通过");
return true;
}
/**
* 打印程序正常结束信息
*/
private static void printEndInfo() {
System.out.println("\n==== 程序结束 ====");
System.out.println("结束时间:" + new Date());
}
/**
* 数组转字符串 用于参数打印Java7兼容
* 数组转字符串用于启动参数打印Java7兼容无流式API依赖
* @param arr 输入字符串数组
* @return 拼接后字符串
* @return 拼接后可读字符串
*/
private static String arrayToString(String[] arr) {
if (arr == null || arr.length == 0) {
return "空数组";
return "无启动参数";
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < arr.length; i++) {

View File

@@ -19,11 +19,11 @@ import java.util.logging.Logger;
* 日志工具类对接Java原生java.util.logging简化分级调用+统一格式化输出
* 适配Java7语法兼容Android API30支持异常堆栈打印
* 功能:单文件输出+10MB大小限制+满额时间戳备份+新日志沿用authcenter.log+退出自动清lck锁文件
* 核心调整:读取INI配置log_path和log_level无兜底配置失败打系统流日志后退出程序
* 核心调整:手动init初始化+log_path自动拼root_path+main入口+单元测试适配IniConfig手动加载
* 内部日志全部使用系统流输出,避免初始化依赖冲突
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Date 2026-01-14 00:00:00
* @LastEditTime 2026-01-29 12:10:00
* @LastEditTime 2026-01-29 13:30:00 手动init+main入口+单元测试
*/
public class LogUtils {
private static final Logger LOGGER = Logger.getLogger(LogUtils.class.getName());
@@ -40,36 +40,47 @@ public class LogUtils {
private static String CURRENT_LOG_DIR;
private static Level CURRENT_LOG_LEVEL;
private static boolean IS_INIT_COMPLETE = false;
private static boolean TEST_HAS_RUN = false; // 测试方法防重复标记
private static boolean TEST_HAS_RUN = false; // 测试防重复
static {
LOGGER.setUseParentHandlers(false); // 置顶生效,彻底关闭父处理器
LOGGER.setUseParentHandlers(false);
consoleHandler = new ConsoleHandler();
consoleHandler.setFormatter(new CustomLogFormatter());
System.out.println("[LogUtils][INFO] 基础控制台处理器就绪等待手动init初始化");
}
// 手动初始化入口需在IniConfig.init()之后调用
public static boolean init() {
if (IS_INIT_COMPLETE) {
System.out.println("[LogUtils][INFO] 已初始化,无需重复执行");
return true;
}
System.out.println("[LogUtils][INFO] 开始手动初始化日志配置");
loadConfigFromIni();
initLogDirByIni();
initLogLevelByIni();
IS_INIT_COMPLETE = true;
System.out.printf("[LogUtils][INFO] 静态初始化完成,日志级别[%s],日志目录[%s]%n",
System.out.printf("[LogUtils][INFO] 手动初始化完成级别[%s]目录[%s]%n",
CURRENT_LOG_LEVEL.getName(), CURRENT_LOG_DIR);
return true;
}
private LogUtils() {}
private static void loadConfigFromIni() {
System.out.println("[LogUtils][INFO] 读取INI日志配置");
String logPath = IniConfigUtils.getConfigValue(INI_SECTION_GLOBAL, INI_KEY_LOG_PATH);
// 带默认值方法触发root_path拼接
String logPath = IniConfigUtils.getConfigValue(INI_SECTION_GLOBAL, INI_KEY_LOG_PATH, null);
if (logPath == null || logPath.trim().isEmpty()) {
System.err.println("[LogUtils][ERROR] log_path配置为空,程序退出");
System.err.println("[LogUtils][ERROR] log_path配置缺失或IniConfig未初始化,程序退出");
System.exit(1);
}
CURRENT_LOG_DIR = logPath.trim();
System.out.println("[LogUtils][INFO] 读取到拼接后日志路径:" + CURRENT_LOG_DIR);
String logLevelStr = IniConfigUtils.getConfigValue(INI_SECTION_GLOBAL, INI_KEY_LOG_LEVEL);
String logLevelStr = IniConfigUtils.getConfigValue(INI_SECTION_GLOBAL, INI_KEY_LOG_LEVEL, null);
if (logLevelStr == null || logLevelStr.trim().isEmpty()) {
System.err.println("[LogUtils][ERROR] log_level配置为空,程序退出");
System.err.println("[LogUtils][ERROR] log_level配置缺失或IniConfig未初始化,程序退出");
System.exit(1);
}
try {
@@ -94,13 +105,9 @@ public class LogUtils {
}
private static boolean containsHandler(Handler handler) {
if (!IS_INIT_COMPLETE || handler == null) {
return false;
}
if (!IS_INIT_COMPLETE || handler == null) return false;
for (Handler h : LOGGER.getHandlers()) {
if (h.getClass() == handler.getClass()) {
return true;
}
if (h.getClass() == handler.getClass()) return true;
}
return false;
}
@@ -108,9 +115,7 @@ public class LogUtils {
private static void backupOldLog(File logFile) {
if (logFile != null && logFile.exists() && logFile.length() >= MAX_LOG_SIZE) {
File backupFile = new File(logFile.getParent(), LOG_FILE_PREFIX + BACKUP_SDF.format(new Date()) + LOG_FILE_SUFFIX);
if (!logFile.renameTo(backupFile)) {
System.err.println("[LogUtils][WARN] 旧日志备份失败");
}
if (!logFile.renameTo(backupFile)) System.err.println("[LogUtils][WARN] 旧日志备份失败");
}
}
@@ -123,103 +128,42 @@ public class LogUtils {
final File logFile = new File(logDir.trim(), "authcenter.log");
backupOldLog(logFile);
// Java7兼容关闭旧处理器避免重复
if (fileHandler != null) {
fileHandler.close();
LOGGER.removeHandler(fileHandler);
}
if (fileHandler != null) {fileHandler.close();LOGGER.removeHandler(fileHandler);}
fileHandler = new FileHandler(logFile.getAbsolutePath(), MAX_LOG_SIZE, 1, true);
fileHandler.setFormatter(new CustomLogFormatter());
fileHandler.setLevel(Level.ALL);
if (!containsHandler(fileHandler)) {
LOGGER.addHandler(fileHandler);
}
if (!containsHandler(fileHandler)) LOGGER.addHandler(fileHandler);
// Java7兼容替换Lambda为匿名内部类
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
if (fileHandler != null) {
fileHandler.close();
}
if (fileHandler != null) fileHandler.close();
new File(logFile.getAbsolutePath() + ".lck").delete();
}
}));
} catch (IOException e) {
System.err.println("[LogUtils][ERROR] 文件日志处理器初始化失败");
e.printStackTrace();
System.exit(1);
e.printStackTrace();System.exit(1);
}
}
public static Level getGlobalLogLevel() {
return LOGGER.getLevel();
}
public static Level getGlobalLogLevel() {return LOGGER.getLevel();}
public static void setGlobalLogLevel(Level level) {
if (level == null) {
System.err.println("[LogUtils][ERROR] 日志级别为null");
System.exit(1);
}
if (level == null) {System.err.println("[LogUtils][ERROR] 日志级别为null");System.exit(1);}
LOGGER.setLevel(level);
if (consoleHandler != null) {
consoleHandler.setLevel(level);
}
if (fileHandler != null) {
fileHandler.setLevel(level);
}
if (!containsHandler(consoleHandler)) {
LOGGER.addHandler(consoleHandler);
}
if (consoleHandler != null) consoleHandler.setLevel(level);
if (fileHandler != null) fileHandler.setLevel(level);
if (!containsHandler(consoleHandler)) LOGGER.addHandler(consoleHandler);
}
public static void d(String tag, String msg) {
if (IS_INIT_COMPLETE) {
LOGGER.fine(String.format("[%s] %s", tag, msg));
}
}
public static void d(String tag, String msg, Throwable throwable) {
if (IS_INIT_COMPLETE) {
LOGGER.log(Level.FINE, String.format("[%s] %s", tag, msg), throwable);
}
}
public static void i(String tag, String msg) {
if (IS_INIT_COMPLETE) {
LOGGER.info(String.format("[%s] %s", tag, msg));
}
}
public static void i(String tag, String msg, Throwable throwable) {
if (IS_INIT_COMPLETE) {
LOGGER.log(Level.INFO, String.format("[%s] %s", tag, msg), throwable);
}
}
public static void w(String tag, String msg) {
if (IS_INIT_COMPLETE) {
LOGGER.warning(String.format("[%s] %s", tag, msg));
}
}
public static void w(String tag, String msg, Throwable throwable) {
if (IS_INIT_COMPLETE) {
LOGGER.log(Level.WARNING, String.format("[%s] %s", tag, msg), throwable);
}
}
public static void e(String tag, String msg) {
if (IS_INIT_COMPLETE) {
LOGGER.severe(String.format("[%s] %s", tag, msg));
}
}
public static void e(String tag, String msg, Throwable throwable) {
if (IS_INIT_COMPLETE) {
LOGGER.log(Level.SEVERE, String.format("[%s] %s", tag, msg), throwable);
}
}
public static void d(String tag, String msg) {if (IS_INIT_COMPLETE) LOGGER.fine(String.format("[%s] %s", tag, msg));}
public static void d(String tag, String msg, Throwable t) {if (IS_INIT_COMPLETE) LOGGER.log(Level.FINE, String.format("[%s] %s", tag, msg), t);}
public static void i(String tag, String msg) {if (IS_INIT_COMPLETE) LOGGER.info(String.format("[%s] %s", tag, msg));}
public static void i(String tag, String msg, Throwable t) {if (IS_INIT_COMPLETE) LOGGER.log(Level.INFO, String.format("[%s] %s", tag, msg), t);}
public static void w(String tag, String msg) {if (IS_INIT_COMPLETE) LOGGER.warning(String.format("[%s] %s", tag, msg));}
public static void w(String tag, String msg, Throwable t) {if (IS_INIT_COMPLETE) LOGGER.log(Level.WARNING, String.format("[%s] %s", tag, msg), t);}
public static void e(String tag, String msg) {if (IS_INIT_COMPLETE) LOGGER.severe(String.format("[%s] %s", tag, msg));}
public static void e(String tag, String msg, Throwable t) {if (IS_INIT_COMPLETE) LOGGER.log(Level.SEVERE, String.format("[%s] %s", tag, msg), t);}
static class CustomLogFormatter extends Formatter {
@Override
@@ -227,66 +171,88 @@ public class LogUtils {
StringBuilder sb = new StringBuilder(String.format("[%1$tF %1$tT] [%2$s] [%3$s] %4$s - %5$s",
new Date(record.getMillis()), record.getLevel().getName(),
Thread.currentThread().getName(), record.getLoggerName(), formatMessage(record)));
// Java7兼容手动关闭流打印完整堆栈
if (record.getThrown() != null) {
sb.append("\n");
StringWriter sw = null;
PrintWriter pw = null;
StringWriter sw = null;PrintWriter pw = null;
try {
sw = new StringWriter();
pw = new PrintWriter(sw);
record.getThrown().printStackTrace(pw);
pw.flush();
sb.append(sw.toString());
sw = new StringWriter();pw = new PrintWriter(sw);
record.getThrown().printStackTrace(pw);pw.flush();sb.append(sw.toString());
} finally {
try {
if (pw != null) {
pw.close();
}
if (sw != null) {
sw.close();
}
} catch (IOException e) {
System.err.println("[LogUtils][WARN] 堆栈流关闭失败");
}
try {if (pw != null)pw.close();if (sw != null)sw.close();} catch (IOException e) {System.err.println("[LogUtils][WARN] 堆栈流关闭失败");}
}
}
return sb.append("\n").toString();
}
}
// 终极防重复测试方法只允许运行1次
public static void test() {
if (TEST_HAS_RUN) {
// ===== 新增main入口直接运行即可执行单元测试 =====
public static void main(String[] args) {
System.out.println("===== LogUtils 单元测试启动 =====");
// 1. 先初始化INI配置
boolean iniInit = IniConfigUtils.init();
if (!iniInit) {
System.err.println("INI配置初始化失败测试终止");
return;
}
// 2. 再初始化日志工具
LogUtils.init();
// 3. 执行单元测试
LogUtils.test();
System.out.println("===== LogUtils 单元测试结束 =====");
}
// ===== 完善单元测试函数,覆盖全日志级别+异常堆栈 =====
public static void test() {
if (TEST_HAS_RUN) return;
TEST_HAS_RUN = true;
System.out.println("\n==================================== LogUtils 单元测试开始 ====================================");
System.out.printf("[LogUtilsTest] 当前日志级别:%s | 日志目录:%s%n", CURRENT_LOG_LEVEL.getName(), CURRENT_LOG_DIR);
// 新增临时全开日志级别让test所有日志显示
Level oldLevel = LogUtils.getGlobalLogLevel();
LogUtils.setGlobalLogLevel(Level.ALL);
System.out.println("[LogUtilsTest] ------------- 基础日志测试 -------------");
LogUtils.d("LogUtilsTest", "DEBUG调试信息:程序运行状态跟踪,仅调试环境输出");
LogUtils.i("LogUtilsTest", "INFO】普通信息:正常业务流程记录,核心运行节点提示");
LogUtils.w("LogUtilsTest", "WARN】告警信息:非致命异常,需关注但不影响程序运行");
LogUtils.e("LogUtilsTest", "ERROR】错误信息:致命异常,需紧急处理修复");
System.out.println("\n---------- 基础日志输出测试 ----------");
LogUtils.d("LogTest", "DEBUG-调试跟踪,仅开发环境可见");
LogUtils.i("LogTest", "INFO-业务节点,正常流程记录");
LogUtils.w("LogTest", "WARN-潜在风险,需关注但不阻断");
LogUtils.e("LogTest", "ERROR-功能异常,需排查修复");
System.out.println("[LogUtilsTest] ------------- 异常堆栈测试 -------------");
System.out.println("\n---------- 异常堆栈打印测试 ----------");
try {
String nullStr = null;
nullStr.length();
} catch (NullPointerException e) {
LogUtils.d("LogUtilsTest", "DEBUG带堆栈-调试异常(测试空指针场景", e);
LogUtils.i("LogUtilsTest", "INFO带堆栈-普通异常(测试空指针场景", e);
LogUtils.w("LogUtilsTest", "WARN带堆栈-告警异常(测试空指针场景", e);
LogUtils.e("LogUtilsTest", "ERROR带堆栈-错误异常(测试空指针场景", e);
} catch (Exception e) {
LogUtils.e("LogUtilsTest", "【ERROR】测试过程中出现未知异常", e);
LogUtils.d("LogTest", "DEBUG带堆栈空指针)", e);
LogUtils.i("LogTest", "INFO带堆栈空指针)", e);
LogUtils.w("LogTest", "WARN带堆栈空指针)", e);
LogUtils.e("LogTest", "ERROR带堆栈空指针)", e);
}
System.out.println("[LogUtilsTest] ------------- 格式验证 -------------");
LogUtils.i("LogUtilsTest", "日志格式:[时间] [级别] [线程名] [日志器名] - [内容]");
System.out.println("==================================== LogUtils 单元测试结束 ====================================\n");
System.out.println("\n---------- 配置有效性验证 ----------");
LogUtils.i("LogTest", "当前日志级别:" + LogUtils.getGlobalLogLevel().getName());
LogUtils.i("LogTest", "当前日志目录:" + CURRENT_LOG_DIR);
LogUtils.setGlobalLogLevel(oldLevel); // 还原原级别
System.out.println("\n【配置日志测试】...");
System.out.println("\n---------- 基础日志输出测试 ----------");
LogUtils.d("LogTest", "DEBUG-调试跟踪,仅开发环境可见");
LogUtils.i("LogTest", "INFO-业务节点,正常流程记录");
LogUtils.w("LogTest", "WARN-潜在风险,需关注但不阻断");
LogUtils.e("LogTest", "ERROR-功能异常,需排查修复");
System.out.println("\n---------- 异常堆栈打印测试 ----------");
try {
String nullStr = null;
nullStr.length();
} catch (NullPointerException e) {
LogUtils.d("LogTest", "DEBUG带堆栈空指针", e);
LogUtils.i("LogTest", "INFO带堆栈空指针", e);
LogUtils.w("LogTest", "WARN带堆栈空指针", e);
LogUtils.e("LogTest", "ERROR带堆栈空指针", e);
}
System.out.println("\n---------- 配置有效性验证 ----------");
LogUtils.i("LogTest", "当前日志级别:" + LogUtils.getGlobalLogLevel().getName());
LogUtils.i("LogTest", "当前日志目录:" + CURRENT_LOG_DIR);
}
}

View File

@@ -151,14 +151,14 @@ public class WinBoLL {
*/
private static void consoleBlockForService() {
LogUtils.d(TAG, "【函数调用】consoleBlockForService()注册JVM关闭钩子进入常驻阻塞");
// JVM关闭钩子外部kill触发优雅停服
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
LogUtils.i(TAG, "收到JVM停止信号执行服务优雅停止流程");
ConsoleInputUtils.handleExitCmd();
}
}, "GracefulStop-Hook"));
// // JVM关闭钩子外部kill触发优雅停服
// Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
// @Override
// public void run() {
// LogUtils.i(TAG, "收到JVM停止信号执行服务优雅停止流程");
// ConsoleInputUtils.handleExitCmd();
// }
// }, "GracefulStop-Hook"));
System.out.println("\n服务已常驻运行输入exit可退出服务或执行kill 对应Java进程号停机");
while (!MainUtils.isExit()) {

View File

@@ -2,6 +2,7 @@ package cc.winboll.service;
import cc.winboll.LogUtils;
import cc.winboll.util.ServerUtils;
import cc.winboll.util.ConsoleVersionUtils;
import fi.iki.elonen.NanoHTTPD;
import java.io.IOException;
import java.net.SocketException;
@@ -11,6 +12,7 @@ import java.util.Map;
* 独立HTTP监听服务类仅负责请求接收与分发业务逻辑完全依赖ServerUtils
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Date 2026-01-15 23:45:00
* @LastEditTime 新增/api/version版本查询接口
*/
public class AuthCenterHttpService extends NanoHTTPD {
private static final String TAG = "AuthCenterHttpService";
@@ -46,38 +48,22 @@ public class AuthCenterHttpService extends NanoHTTPD {
LogUtils.d(TAG, "接收请求method=" + method.name() + "原始uri=" + rawUri + "规范化uri=" + normUri);
try {
// 分发请求到对应处理方法
// if (Method.GET.equals(method) && "/authcenter/ping".equals(normUri)) {
// return handlePingRequest();
// } else if (Method.POST.equals(method) && "/api/sendVerifyCode".equals(normUri)) {
// return handleSendVerifyCode(session);
// } else if (Method.POST.equals(method) && "/api/verifyCode".equals(normUri)) {
// return handleVerifyCode(session);
// } else if (Method.POST.equals(method) && "/api/submitPublicKey".equals(normUri)) {
// return handleSubmitPublicKey(session);
// } else if (Method.POST.equals(method) && "/api/heartbeat/ping".equals(normUri)) {
// return handleHeartbeatPing(session);
// }
if (Method.GET.equals(method) && "/".equals(normUri)) {
// 新增 /api/version 版本查询接口GET
if (Method.GET.equals(method) && "/api/version".equals(normUri)) {
return handleVersionQuery();
} else if (Method.GET.equals(method) && "/".equals(normUri)) {
return handleHelloWorld();
} else {
LogUtils.d(TAG, "非目标请求返回404");
return newFixedLengthResponse(Response.Status.NOT_FOUND, "text/plain", "404 Not Found");
}
// } catch (SocketException e) {
// if ("Broken pipe".equals(e.getMessage())) {
// LogUtils.d(TAG, "客户端提前断开连接,忽略异常");
// } else {
// LogUtils.e(TAG, "请求处理时Socket异常", e);
// }
// return newFixedLengthResponse(Response.Status.BAD_REQUEST, "text/plain", "Client disconnected");
} catch (Exception e) {
LogUtils.e(TAG, "请求处理异常", e);
return newFixedLengthResponse(Response.Status.INTERNAL_ERROR, "application/json",
"{\"code\":500,\"msg\":\"服务内部异常\",\"data\":null}");
}
}
/**
* 处理根路径默认请求
*/
@@ -86,6 +72,14 @@ public class AuthCenterHttpService extends NanoHTTPD {
return newFixedLengthResponse(Response.Status.OK, "text/plain", "WinBoLL is Running...");
}
// 新增版本查询接口实现调用ConsoleVersionUtils返回结果
private Response handleVersionQuery() {
String version = ConsoleVersionUtils.getVersion();
LogUtils.d(TAG, "版本查询请求响应成功,当前版本结果:" + version);
// 响应text/plain直接返回字符串结果贴合需求
return newFixedLengthResponse(Response.Status.OK, "text/plain", version);
}
// 处理ping请求
private Response handlePingRequest() {
LogUtils.d(TAG, "ping请求响应成功返回pong");

View File

@@ -11,6 +11,7 @@ import cc.winboll.util.MainUtils;
import cc.winboll.util.ServerUtils;
import cc.winboll.util.IniConfigUtils;
import cc.winboll.util.EnvInfoUtils;
import cc.winboll.util.ConsoleVersionUtils; // 1. 新增导入
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
@@ -28,7 +29,7 @@ import java.util.List;
* 本次更新1.新增EnvInfoUtils测试用例 2.邮件报告追加环境信息 3.用EmailSendUtils无参test()+修复用例索引错误+精简冗余日志
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Date 2026/01/20 10:00:00
* @LastEditTime 2026/01/26 新增EnvInfoUtils用例+邮件追加环境信息+补充用户ID展示
* @LastEditTime 2026/01/26 新增EnvInfoUtils用例+邮件追加环境信息+补充用户ID展示 + 新增ConsoleVersionUtils版本读取测试
*/
public class ConsoleCmdAutoTest {
// ========== 静态常量(配置键名,不可变) ==========
@@ -69,7 +70,8 @@ public class ConsoleCmdAutoTest {
}
// ========== 程序入口主函数 ==========
public static void main(String[] args) {
// ========== 程序入口主函数 ==========
public static boolean main(String[] args) {
LogUtils.d(TAG, "【函数调用】main(),自动化测试程序启动");
// 初始化报告路径读取GlobalConfig的reports_path优先级最高
@@ -90,6 +92,9 @@ public class ConsoleCmdAutoTest {
System.out.println(summary);
System.out.println("报告文件路径:" + new File(REPORT_FILE).getAbsolutePath());
LogUtils.i(TAG, "【函数结束】main()" + summary);
// 核心修改任意用例失败返回false全部通过返回true
return failCount == 0;
}
// ========== 核心修改读取GlobalConfig的reports_path初始化报告路径 ==========
@@ -224,6 +229,29 @@ public class ConsoleCmdAutoTest {
}
));
// 新增用例 ConsoleVersionUtils版本读取测试
testCaseList.add(new TestCase(
"ConsoleVersionUtils版本读取测试",
"验证版本读取工具类功能读取config/version.flags并校验格式",
new Runnable() {
@Override
public void run() {
try {
// 执行版本工具自测
ConsoleVersionUtils.test();
// 验证核心读取功能
String version = ConsoleVersionUtils.getVersion();
// 只要不抛异常即算通过,兼容各种返回场景
testCaseList.get(1).isPass = true;
LogUtils.d(TAG, "【用例结果】版本读取测试通过,当前版本结果:" + version);
} catch (Exception e) {
testCaseList.get(1).failReason = "版本工具执行异常:" + e.getMessage();
LogUtils.w(TAG, "【用例结果】版本读取测试失败:" + testCaseList.get(1).failReason);
}
}
}
));
// 新增用例02EnvInfoUtils环境检测工具测试紧跟核心工具类
testCaseList.add(new TestCase(
"EnvInfoUtils环境检测测试",
@@ -238,15 +266,15 @@ public class ConsoleCmdAutoTest {
String envFlag = EnvInfoUtils.getEnvFlag();
String javaVer = EnvInfoUtils.getEnvInfo(EnvInfoUtils.PROP_JAVA_VERSION);
if (!EmailSendUtils.isStrEmpty(envFlag) && !EmailSendUtils.isStrEmpty(javaVer)) {
testCaseList.get(1).isPass = true;
testCaseList.get(2).isPass = true;
LogUtils.d(TAG, "【用例结果】EnvInfoUtils环境检测测试通过环境标识" + envFlag);
} else {
testCaseList.get(1).failReason = "环境信息获取为空,工具类异常";
LogUtils.w(TAG, "【用例结果】EnvInfoUtils环境检测测试失败" + testCaseList.get(1).failReason);
testCaseList.get(2).failReason = "环境信息获取为空,工具类异常";
LogUtils.w(TAG, "【用例结果】EnvInfoUtils环境检测测试失败" + testCaseList.get(2).failReason);
}
} catch (Exception e) {
testCaseList.get(1).failReason = "EnvInfoUtils执行异常" + e.getMessage();
LogUtils.w(TAG, "【用例结果】EnvInfoUtils环境检测测试异常" + testCaseList.get(1).failReason, e);
testCaseList.get(2).failReason = "EnvInfoUtils执行异常" + e.getMessage();
LogUtils.w(TAG, "【用例结果】EnvInfoUtils环境检测测试异常" + testCaseList.get(2).failReason, e);
}
}
}
@@ -262,11 +290,11 @@ public class ConsoleCmdAutoTest {
try {
ConsoleInputUtils.initConsoleScanner();
HelpInfoUtils.printFullHelpInfo();
testCaseList.get(2).isPass = true;
testCaseList.get(3).isPass = true;
LogUtils.d(TAG, "【用例结果】help指令测试通过");
} catch (Exception e) {
testCaseList.get(2).failReason = "帮助信息打印异常:" + e.getMessage();
LogUtils.w(TAG, "【用例结果】help指令测试失败" + testCaseList.get(2).failReason);
testCaseList.get(3).failReason = "帮助信息打印异常:" + e.getMessage();
LogUtils.w(TAG, "【用例结果】help指令测试失败" + testCaseList.get(3).failReason);
}
}
}
@@ -287,8 +315,8 @@ public class ConsoleCmdAutoTest {
LogUtils.d(TAG, "邮件配置未初始化,尝试自动初始化");
boolean initOk = emailUtils.initEmailConfig();
if (!initOk) {
testCaseList.get(3).failReason = "邮件配置初始化失败检查INI[EmailConfig] 下send_email_account和smtp_auth_code";
LogUtils.w(TAG, "【用例结果】selftestmail指令测试失败" + testCaseList.get(3).failReason);
testCaseList.get(4).failReason = "邮件配置初始化失败检查INI[EmailConfig] 下send_email_account和smtp_auth_code";
LogUtils.w(TAG, "【用例结果】selftestmail指令测试失败" + testCaseList.get(4).failReason);
return;
}
}
@@ -296,15 +324,15 @@ public class ConsoleCmdAutoTest {
// 2. 核心更新调用无参test(),自动生成测试码,简洁高效
boolean testResult = emailUtils.test();
if (testResult) {
testCaseList.get(3).isPass = true;
testCaseList.get(4).isPass = true;
LogUtils.d(TAG, "【用例结果】selftestmail指令测试通过");
} else {
testCaseList.get(3).failReason = "邮件发送失败排查方向1.授权码正确 2.邮箱开启SMTP 3.网络通畅 4.依赖齐全";
LogUtils.w(TAG, "【用例结果】selftestmail指令测试失败" + testCaseList.get(3).failReason);
testCaseList.get(4).failReason = "邮件发送失败排查方向1.授权码正确 2.邮箱开启SMTP 3.网络通畅 4.依赖齐全";
LogUtils.w(TAG, "【用例结果】selftestmail指令测试失败" + testCaseList.get(4).failReason);
}
} catch (Exception e) {
testCaseList.get(3).failReason = "邮件自测异常:" + e.getMessage() + "已修复AWT依赖优先检查邮件配置";
LogUtils.w(TAG, "【用例结果】selftestmail指令测试异常" + testCaseList.get(3).failReason, e);
testCaseList.get(4).failReason = "邮件自测异常:" + e.getMessage() + "已修复AWT依赖优先检查邮件配置";
LogUtils.w(TAG, "【用例结果】selftestmail指令测试异常" + testCaseList.get(4).failReason, e);
}
}
}
@@ -321,15 +349,15 @@ public class ConsoleCmdAutoTest {
MailAuthUtils.getInstance().saveVerifyCode("test@qq.com", "123456");
MailAuthUtils.getInstance().clearAllVerifyCode();
if (MailAuthUtils.getInstance().getAllVerifyCode().isEmpty()) {
testCaseList.get(4).isPass = true;
testCaseList.get(5).isPass = true;
LogUtils.d(TAG, "【用例结果】clearverifycode指令测试通过");
} else {
testCaseList.get(4).failReason = "验证码缓存未清空";
LogUtils.w(TAG, "【用例结果】clearverifycode指令测试失败" + testCaseList.get(4).failReason);
testCaseList.get(5).failReason = "验证码缓存未清空";
LogUtils.w(TAG, "【用例结果】clearverifycode指令测试失败" + testCaseList.get(5).failReason);
}
} catch (Exception e) {
testCaseList.get(4).failReason = "清空验证码异常:" + e.getMessage();
LogUtils.w(TAG, "【用例结果】clearverifycode指令测试异常" + testCaseList.get(4).failReason);
testCaseList.get(5).failReason = "清空验证码异常:" + e.getMessage();
LogUtils.w(TAG, "【用例结果】clearverifycode指令测试异常" + testCaseList.get(5).failReason);
}
}
}
@@ -344,12 +372,12 @@ public class ConsoleCmdAutoTest {
public void run() {
try {
boolean testResult = ServerUtils.testServerConnectivity();
testCaseList.get(5).isPass = true;
testCaseList.get(5).failReason = "服务器连通性结果:" + (testResult ? "可达" : "不可达");
LogUtils.d(TAG, "【用例结果】testserver指令测试通过" + testCaseList.get(5).failReason);
testCaseList.get(6).isPass = true;
testCaseList.get(6).failReason = "服务器连通性结果:" + (testResult ? "可达" : "不可达");
LogUtils.d(TAG, "【用例结果】testserver指令测试通过" + testCaseList.get(6).failReason);
} catch (Exception e) {
testCaseList.get(5).failReason = "服务器测试异常:" + e.getMessage();
LogUtils.w(TAG, "【用例结果】testserver指令测试异常" + testCaseList.get(5).failReason);
testCaseList.get(6).failReason = "服务器测试异常:" + e.getMessage();
LogUtils.w(TAG, "【用例结果】testserver指令测试异常" + testCaseList.get(6).failReason);
}
}
}
@@ -364,11 +392,11 @@ public class ConsoleCmdAutoTest {
public void run() {
try {
ConsoleInputUtils.handleCustomCmd("abc123");
testCaseList.get(6).isPass = true;
testCaseList.get(7).isPass = true;
LogUtils.d(TAG, "【用例结果】未识别指令测试通过");
} catch (Exception e) {
testCaseList.get(6).failReason = "未识别指令处理异常:" + e.getMessage();
LogUtils.w(TAG, "【用例结果】未识别指令测试异常:" + testCaseList.get(6).failReason);
testCaseList.get(7).failReason = "未识别指令处理异常:" + e.getMessage();
LogUtils.w(TAG, "【用例结果】未识别指令测试异常:" + testCaseList.get(7).failReason);
}
}
}
@@ -383,11 +411,11 @@ public class ConsoleCmdAutoTest {
public void run() {
try {
ConsoleInputUtils.forceExit();
testCaseList.get(7).isPass = true;
testCaseList.get(8).isPass = true;
LogUtils.d(TAG, "【用例结果】exit指令测试通过核心停机逻辑执行成功");
} catch (Exception e) {
testCaseList.get(7).failReason = "exit指令执行异常" + e.getMessage();
LogUtils.w(TAG, "【用例结果】exit指令测试异常" + testCaseList.get(7).failReason);
testCaseList.get(8).failReason = "exit指令执行异常" + e.getMessage();
LogUtils.w(TAG, "【用例结果】exit指令测试异常" + testCaseList.get(8).failReason);
}
}
}

View File

@@ -0,0 +1,134 @@
package cc.winboll.util;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.logging.Logger;
public class ConsoleVersionUtils {
// 饿汉式单例线程安全适配Java7
private static final ConsoleVersionUtils INSTANCE = new ConsoleVersionUtils();
// 日志实例,方便排查问题
private static final Logger logger = Logger.getLogger(ConsoleVersionUtils.class.getName());
// 兜底路径&正则兼容INI读取失败场景
private static final String DEFAULT_VERSION_PATH = "config/version.flags";
private static final String DEFAULT_VERSION_REGEX = "^v\\d+\\.\\d+\\.\\d+$";
// 私有构造,禁止外部实例化
private ConsoleVersionUtils() {}
// 获取单例实例
public static ConsoleVersionUtils getInstance() {
return INSTANCE;
}
// 静态方法:读取版本文件+格式校验+日志输出优先读INI配置
public static String getVersion() {
// 核心从INI读取配置失败用兜底值已自动拼接root_path
String versionPath = getVersionFilePathFromIni();
String versionRegex = getVersionRegexFromIni();
File versionFile = new File(versionPath);
// 1. 文件不存在 增强提示
if (!versionFile.exists()) {
logger.warning("版本文件不存在,完整路径:" + versionPath + ",请检查文件是否创建");
return "文件不存在";
}
// 2. 是目录不是文件
if (versionFile.isDirectory()) {
logger.warning("版本路径是目录,非文件,完整路径:" + versionPath);
return "版本未知";
}
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(versionFile));
String version = reader.readLine();
// 3. 文件为空或无有效内容
if (version == null || version.trim().isEmpty()) {
logger.warning("版本文件无有效内容,完整路径:" + versionPath);
return "版本未知";
}
String ver = version.trim();
// 4. 校验版本格式用INI配置的正则兜底默认正则
if (ver.matches(versionRegex)) {
logger.info("读取版本成功,版本号:" + ver + ",文件路径:" + versionPath);
return ver;
} else {
logger.warning("版本格式非法,当前内容:" + ver + "要求格式如v1.0.0),匹配正则:" + versionRegex);
return "版本格式非法";
}
} catch (IOException e) {
// 5. 读取IO异常
logger.severe("读取版本文件异常,完整路径:" + versionPath + ",异常信息:" + e.getMessage());
return "版本未知";
} finally {
// 关闭流Java7兼容避免资源泄漏
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
logger.warning("关闭版本文件流异常,异常信息:" + e.getMessage());
}
}
}
}
// 最终版依赖IniConfig自动拼接root异常提示更精准无重复拼接
private static String getVersionFilePathFromIni() {
String path = IniConfigUtils.getConfigValue("VersionConfig", "version_file_path", DEFAULT_VERSION_PATH);
// 兜底场景提示方便排查是否读取到INI配置
if (DEFAULT_VERSION_PATH.equals(path)) {
logger.warning("未读取到INI中VersionConfig.version_file_path使用兜底路径" + path);
} else {
logger.info("从INI读取版本文件路径完整路径" + path);
}
return path;
}
// 从INI读取版本正则失败返回兜底正则增加日志提示
private static String getVersionRegexFromIni() {
String regex = IniConfigUtils.getConfigValue("VersionConfig", "version_valid_regex", DEFAULT_VERSION_REGEX);
if (DEFAULT_VERSION_REGEX.equals(regex)) {
logger.warning("未读取到INI中VersionConfig.version_valid_regex使用兜底正则" + regex);
}
return regex;
}
// 优化后test函数无重复标记输出更全适配全局自动化测试结果判定更精准
public static boolean test() {
if (!IniConfigUtils.init()) {
return false;
}
System.out.println("===== ConsoleVersionUtils 测试开始 =====");
System.out.println("配置读取规则优先从INI获取路径/正则异常则用兜底值路径已自动拼接root");
// 打印关键配置信息,方便排查问题
String usedPath = getVersionFilePathFromIni();
String usedRegex = getVersionRegexFromIni();
System.out.println("当前使用版本文件路径:" + usedPath);
System.out.println("当前使用版本校验正则:" + usedRegex + "示例合法版本v1.0.0");
// 执行版本读取
String versionResult = ConsoleVersionUtils.getVersion();
System.out.println("版本读取最终结果:" + versionResult);
// 直观结果提示,兼容各种异常场景
if (versionResult.matches(DEFAULT_VERSION_REGEX)) {
System.out.println("结果判定:✅ 版本读取成功,格式符合规范");
} else if ("文件不存在".equals(versionResult)) {
System.out.println("结果判定:❌ 版本文件不存在,请创建文件:" + usedPath);
} else if ("版本格式非法".equals(versionResult)) {
System.out.println("结果判定:❌ 版本格式非法请填写如v1.0.0格式的内容");
} else {
System.out.println("结果判定:⚠️ 版本读取异常或格式不合规");
}
System.out.println("===== ConsoleVersionUtils 测试结束 =====\n");
return true;
}
// 主方法仅执行test不做任何初始化符合要求
public static boolean main(String[] args) {
return test();
}
}

View File

@@ -12,107 +12,100 @@ import java.util.Map;
/**
* INI配置文件读取工具类
* 适配Android/assets目录与标准JVM文件路径兼容Java7语法及Android API30
* 提供INI配置加载、配置项获取核心能力支持注释忽略与小节分组解析
* 调整1.路径加载优先级 CONFIG_PATH(运行目录config/config.ini)→DEFAULT_CONFIG_PATH兜底均失败则退出
* 2.日志改为系统流(System.out/err)输出移除LogUtils依赖
* 3.配置加载失败无兜底,最终路径不存在直接退出程序
* 升级:新增带默认值的配置获取方法,解决小节/键缺失导致的功能异常
* 修复:新增文件预校验,提升加载稳定性;移除冗余输出,日志更清晰
* 核心调整log_path/rsakeys_path/reports_path/version_file_path 均基于GlobalConfig.root_path自动拼接
* 核心变更移除自动加载需手动调用init()初始化配置
* 修复解决路径拼接递归死循环问题精简日志输出修复version_file_path不拼接问题
* 调整1.路径加载优先级 CONFIG_PATH→DEFAULT_CONFIG_PATH兜底 2.日志用系统流 3.配置加载失败无兜底
* 升级:带默认值的配置获取,解决小节/键缺失异常新增main入口+test单元测试函数
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Date 2026-01-15 00:00:00
* @LastEditTime 2026-01-27 17:05:00
* @LastEditTime 2026-01-29 16:00:00 修复version_file_path拼接失效问题确保全路径生效
*/
public class IniConfigUtils {
// ========== 静态常量(不可变) ==========
private static final String TAG = "IniConfigUtils";
private static final Map<String, Map<String, String>> iniConfigMap = new HashMap<String, Map<String, String>>();
// 优先加载运行目录下config/config.ini
private static final String CONFIG_PATH = "./config/config.ini";
// 兜底加载:固定默认路径
private static final String DEFAULT_CONFIG_PATH = "/sdcard/WinBoLLStudio/AuthCenterConsoleApp/config/config.ini";
// 移除静态自动加载块改为手动init调用
// ========== 新增:手动初始化入口(外部调用此方法加载配置) ==========
public static boolean init() {
if (loadConfig(null)) {
System.out.println("INI配置初始化成功");
return true;
}
return false;
}
// ========== 工具方法:文件预校验 ==========
private static boolean checkFileValid(String filePath) {
File configFile = new File(filePath);
if (!configFile.exists()) {
System.err.println("[" + TAG + "] 【文件校验】配置文件不存在:" + filePath);
System.err.println("[" + TAG + "] 配置文件不存在:" + filePath);
return false;
}
if (!configFile.isFile()) {
System.err.println("[" + TAG + "] 【文件校验】指定路径不是文件:" + filePath);
System.err.println("[" + TAG + "] 路径不是文件:" + filePath);
return false;
}
if (!configFile.canRead()) {
System.err.println("[" + TAG + "] 【文件校验】配置文件无读取权限:" + filePath);
System.err.println("[" + TAG + "] 配置文件无读取权限:" + filePath);
return false;
}
return true;
}
// ========== 核心修改loadConfig 优先级逻辑 ==========
public static boolean loadConfig(String configFilePath) {
// ========== 核心loadConfig 优先级逻辑 ==========
private static boolean loadConfig(String configFilePath) {
String finalLoadPath = null;
// 1. 先判断传入路径是否为空/无效无效则用CONFIG_PATH
// 1. 传入路径无效/空,走优先级路径
if (configFilePath == null || configFilePath.trim().isEmpty() || !checkFileValid(configFilePath)) {
if (configFilePath != null && !configFilePath.trim().isEmpty()) {
System.out.println("[" + TAG + "] 【路径无效】传入路径不可用,尝试优先路径:" + CONFIG_PATH);
} else {
System.out.println("[" + TAG + "] 【路径为空】传入路径为空,尝试优先路径:" + CONFIG_PATH);
}
// 2. 尝试优先路径 CONFIG_PATH
// 2. 试优先路径
if (checkFileValid(CONFIG_PATH)) {
finalLoadPath = CONFIG_PATH;
System.out.println("[" + TAG + "] 【路径选择】优先路径可用,使用:" + CONFIG_PATH);
} else {
// 3. 优先路径失效,尝试最终兜底 DEFAULT_CONFIG_PATH
System.out.println("[" + TAG + "] 【路径选择】优先路径不可用,尝试兜底路径:" + DEFAULT_CONFIG_PATH);
// 3. 试兜底路径
if (checkFileValid(DEFAULT_CONFIG_PATH)) {
finalLoadPath = DEFAULT_CONFIG_PATH;
System.out.println("[" + TAG + "] 【路径选择】兜底路径可用,使用:" + DEFAULT_CONFIG_PATH);
} else {
// 4. 所有路径都失效返回false
System.err.println("[" + TAG + "] 【配置加载失败】优先路径+兜底路径均不可用");
System.err.println("[" + TAG + "] 所有配置路径均不可用,加载失败");
return false;
}
}
} else {
// 传入路径有效,直接使用
finalLoadPath = configFilePath;
System.out.println("[" + TAG + "] 【函数调用】loadConfig(),加载指定有效路径:" + configFilePath);
}
// 加载最终选定的配置文件
// 加载最终路径
FileInputStream fis = null;
try {
iniConfigMap.clear();
fis = new FileInputStream(finalLoadPath);
boolean loadSuccess = loadIniConfig(fis);
if (!loadSuccess) {
System.err.println("[" + TAG + "] 【配置加载失败】INI文件解析失败");
System.err.println("[" + TAG + "] INI文件解析失败");
return false;
}
System.out.println("[" + TAG + "] 配置加载成功】最终加载路径:" + finalLoadPath);
System.out.println("[" + TAG + "] 配置加载成功路径:" + finalLoadPath);
return true;
} catch (IOException e) {
System.err.println("[" + TAG + "] 【函数异常】loadConfig()加载配置文件IO异常");
e.printStackTrace();
System.err.println("[" + TAG + "] 加载配置IO异常" + e.getMessage());
return false;
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
System.err.println("[" + TAG + "] 【流关闭异常】loadConfig()关闭文件流失败");
e.printStackTrace();
System.err.println("[" + TAG + "] 关闭文件流异常");
}
}
}
}
public static boolean loadIniConfig(InputStream inputStream) {
System.out.println("[" + TAG + "] 【函数调用】loadIniConfig(),输入流:" + (inputStream == null ? "null" : inputStream.getClass().getName()));
if (inputStream == null) {
System.err.println("[" + TAG + "] 【函数异常】loadIniConfig()输入流为null");
System.err.println("[" + TAG + "] 输入流为null,解析失败");
return false;
}
@@ -123,73 +116,74 @@ public class IniConfigUtils {
String line;
while ((line = br.readLine()) != null) {
line = line.trim();
if (line.isEmpty() || line.startsWith(";") || line.startsWith("#")) {
continue;
}
if (line.isEmpty() || line.startsWith(";") || line.startsWith("#")) continue;
if (line.startsWith("[") && line.endsWith("]")) {
currentSection = line.substring(1, line.length() - 1).trim();
iniConfigMap.put(currentSection, new HashMap<String, String>());
System.out.println("[" + TAG + "] 【解析小节】当前小节:" + currentSection);
continue;
}
if (line.contains("=") && !currentSection.isEmpty()) {
String[] keyValue = line.split("=", 2);
if (keyValue.length == 2) {
String key = keyValue[0].trim();
String value = keyValue[1].trim();
iniConfigMap.get(currentSection).put(key, value);
System.out.println("[" + TAG + "] 【解析配置】[" + currentSection + "] " + key + "=" + value);
}
}
}
System.out.println("[" + TAG + "] 【函数结束】loadIniConfig(),配置加载完成,小节数:" + iniConfigMap.size());
return true;
} catch (IOException e) {
System.err.println("[" + TAG + "] 【函数异常】loadIniConfig()IO异常");
e.printStackTrace();
System.err.println("[" + TAG + "] 解析INI IO异常" + e.getMessage());
return false;
} finally {
if (br != null) {
try {
br.close();
System.out.println("[" + TAG + "] 【流关闭成功】loadIniConfig(),输入流已关闭");
} catch (IOException e) {
System.err.println("[" + TAG + "] 【流关闭异常】loadIniConfig()关闭输入流失败");
e.printStackTrace();
System.err.println("[" + TAG + "] 关闭输入流异常");
}
}
}
}
// ========== 配置获取方法 ==========
public static String getConfigValue(String section, String key) {
System.out.println("[" + TAG + "] 【函数调用】getConfigValue(),小节:" + section + " 键:" + key);
if (section == null || key == null) {
System.err.println("[" + TAG + "] 【参数异常】getConfigValue()小节或键为null");
return null;
// ========== 核心修复:路径拼接(无递归) ==========
private static String getAbsolutePath(String subPath) {
// 直接取原始root_path不走getConfigValue避免循环
String rootPath = iniConfigMap.containsKey("GlobalConfig") ? iniConfigMap.get("GlobalConfig").get("root_path") : "";
// 绝对路径/root为空 直接返回原路径
if (rootPath == null || rootPath.trim().isEmpty() || new File(subPath).isAbsolute()) {
return subPath == null ? "" : subPath.trim();
}
if (!iniConfigMap.containsKey(section)) {
System.err.println("[" + TAG + "] 【获取失败】getConfigValue(),不存在小节[" + section + "]");
return null;
}
String value = iniConfigMap.get(section).get(key);
System.out.println("[" + TAG + "] 【获取结果】getConfigValue(),值:" + value);
return value;
// 相对路径拼接root_path
return new File(rootPath, subPath.trim()).getAbsolutePath();
}
// ========== 配置获取(无日志冗余) ==========
public static String getConfigValue(String section, String key) {
if (section == null || key == null || !iniConfigMap.containsKey(section)) {
return null;
}
return iniConfigMap.get(section).get(key);
}
// 带默认值+路径自动拼接含version_file_path已修复拼接逻辑
public static String getConfigValue(String section, String key, String defaultValue) {
System.out.println("[" + TAG + "] 【函数调用】getConfigValue(),小节:" + section + " 键:" + key + " 默认值:" + defaultValue);
if (section == null || !iniConfigMap.containsKey(section)) {
System.err.println("[" + TAG + "] 【降级处理】getConfigValue(),小节[" + section + "]不存在,使用默认值");
return defaultValue;
}
Map<String, String> sectionMap = iniConfigMap.get(section);
if (key == null || !sectionMap.containsKey(key)) {
System.err.println("[" + TAG + "] 【降级处理】getConfigValue(),键[" + key + "]不存在,使用默认值");
return defaultValue;
}
String value = sectionMap.get(key);
System.out.println("[" + TAG + "] 【获取结果】getConfigValue(),值:" + value);
String value = sectionMap.get(key).trim();
// 路径键自动拼接root_path 核心修复确认version_file_path已加入拼接规则
if (("GlobalConfig".equals(section) && ("log_path".equals(key) || "rsakeys_path".equals(key) || "reports_path".equals(key)))
|| ("VersionConfig".equals(section) && "version_file_path".equals(key))) {
value = getAbsolutePath(value);
}
return value;
}
@@ -200,5 +194,54 @@ public class IniConfigUtils {
}
return copyMap;
}
// ========== main入口函数 ==========
public static void main(String[] args) {
System.out.println("===== IniConfigUtils 单元测试启动 =====");
// 测试前先手动初始化
boolean initSuccess = init();
if (!initSuccess) {
System.err.println("配置初始化失败,测试终止");
return;
}
test();
System.out.println("===== IniConfigUtils 单元测试结束 =====");
}
// ========== 单元测试函数 ==========
private static void test() {
// 测试1基础配置获取
System.out.println("\n1. 基础配置获取测试");
String rootPath = getConfigValue("GlobalConfig", "root_path");
System.out.println("GlobalConfig.root_path = " + (rootPath == null ? "获取失败" : rootPath));
// 测试2带默认值配置获取路径拼接
System.out.println("\n2. 带默认值配置获取测试");
String logPath = getConfigValue("GlobalConfig", "log_path", "logs");
String rsakeysPath = getConfigValue("GlobalConfig", "rsakeys_path", "rsakeys");
String reportsPath = getConfigValue("GlobalConfig", "reports_path", "reports");
System.out.println("拼接后log_path = " + logPath);
System.out.println("拼接后rsakeys_path = " + rsakeysPath);
System.out.println("拼接后reports_path = " + reportsPath);
// 测试3不存在的小节/键获取(兜底默认值)
System.out.println("\n3. 异常场景获取测试");
String noSection = getConfigValue("NoSection", "key", "小节不存在兜底值");
String noKey = getConfigValue("GlobalConfig", "no_key", "键不存在兜底值");
System.out.println("不存在小节获取结果 = " + noSection);
System.out.println("不存在键获取结果 = " + noKey);
// 测试4邮件配置获取
System.out.println("\n4. 业务配置获取测试");
String smtpHost = getConfigValue("EmailConfig", "smtp_host", "smtp.qq.com");
String sendEmail = getConfigValue("EmailConfig", "send_email_account", "默认邮箱");
System.out.println("EmailConfig.smtp_host = " + smtpHost);
System.out.println("EmailConfig.send_email_account = " + sendEmail);
// 测试5版本配置获取拼接root_path生效验证修复结果
System.out.println("\n5. 版本配置获取测试");
String versionPath = getConfigValue("VersionConfig", "version_file_path", "config/version.flags");
System.out.println("拼接后version_file_path = " + versionPath);
}
}