优雅停机,完美实现。
This commit is contained in:
@@ -50,20 +50,3 @@ echo -e "📝 Java日志输出路径:$MAIN_LOG\n"
|
||||
|
||||
# 关键:无任何重定向,让Java的LogUtils负责写日志,终端保留完整交互能力
|
||||
java -cp "$CLASSPATH" "$MAIN_CLASS" -v -log:ALL
|
||||
JAVA_PID=$!
|
||||
EXIT_CODE=$?
|
||||
|
||||
# 7. 等待Java进程结束(解决卡空行问题)
|
||||
wait $JAVA_PID
|
||||
|
||||
# 8. 后置处理
|
||||
stty sane
|
||||
if [ $STOP_SIGNAL -eq 0 ]; then
|
||||
if [ $EXIT_CODE -eq 0 ]; then
|
||||
echo -e "\n✅ 服务优雅停机完成"
|
||||
else
|
||||
echo -e "\n❌ 服务异常退出,退出码:$EXIT_CODE,查看Java日志:$MAIN_LOG"
|
||||
fi
|
||||
fi
|
||||
exit $EXIT_CODE
|
||||
|
||||
|
||||
@@ -1,65 +1,64 @@
|
||||
package cc.winboll;
|
||||
|
||||
import cc.winboll.LogUtils;
|
||||
import cc.winboll.service.AuthService;
|
||||
import cc.winboll.util.ServerUtils;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Scanner;
|
||||
|
||||
/**
|
||||
* 鉴权中心控制台应用入口类
|
||||
* 负责初始化核心组件、提供控制台交互能力、启停鉴权服务、授权码生成与校验
|
||||
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2026/01/15 18:00:00
|
||||
* @LastEditTime 2026/01/15 20:20:00
|
||||
* @Date 2026-01-15 18:00:00
|
||||
* @LastEditTime 2026-01-23 21:46:00
|
||||
*/
|
||||
public class AuthCenterConsoleApp {
|
||||
// ========== 静态属性(按常量→变量排序) ==========
|
||||
private static final String TAG = "AuthCenterConsoleApp";
|
||||
// 默认服务器地址(可通过控制台输入修改)
|
||||
private static final String DEFAULT_SERVER_URL = "http://localhost:8080";
|
||||
private static final int SERVER_PORT = 8080;
|
||||
private static Scanner scanner;
|
||||
private static boolean isServiceRunning = false;
|
||||
|
||||
// ========== 入口方法 ==========
|
||||
public static void main() {
|
||||
// 1. 初始化核心工具与控制台输入
|
||||
LogUtils.d(TAG, "【入口调用】main(),鉴权中心控制台应用启动");
|
||||
initConsoleEnv();
|
||||
// 2. 初始化服务器地址与鉴权核心服务
|
||||
initAuthCenterCore();
|
||||
// 3. 启动控制台交互循环
|
||||
startConsoleInteractive();
|
||||
LogUtils.d(TAG, "【入口结束】main(),鉴权中心控制台应用退出");
|
||||
}
|
||||
|
||||
// ========== 初始化相关函数(按执行顺序排列) ==========
|
||||
/**
|
||||
* 初始化控制台基础环境(日志、输入流)
|
||||
*/
|
||||
private static void initConsoleEnv() {
|
||||
LogUtils.d(TAG, "【函数调用】initConsoleEnv(),开始初始化控制台基础环境");
|
||||
scanner = new Scanner(System.in);
|
||||
LogUtils.d(TAG, "控制台环境初始化完成,输入流已就绪");
|
||||
System.out.println("===== AuthCenter 鉴权中心控制台启动 =====");
|
||||
LogUtils.d(TAG, "【函数结束】initConsoleEnv(),控制台输入流初始化就绪");
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化鉴权核心组件(服务器地址+AuthService服务),补充地址合法性校验
|
||||
* 初始化鉴权核心组件(自动拼接本机IP+8080,补充地址合法性校验)
|
||||
*/
|
||||
private static void initAuthCenterCore() {
|
||||
// 可选:控制台输入服务器地址,默认使用预设地址
|
||||
System.out.printf("请输入服务器地址(默认:%s):%n", DEFAULT_SERVER_URL);
|
||||
String serverUrl = scanner.nextLine().trim();
|
||||
serverUrl = serverUrl.isEmpty() ? DEFAULT_SERVER_URL : serverUrl;
|
||||
LogUtils.d(TAG, "【函数调用】initAuthCenterCore(),开始初始化鉴权核心组件");
|
||||
String serverUrl = getLocalHostIpWithPort();
|
||||
System.out.printf("当前自动适配本机服务地址:%s%n", serverUrl);
|
||||
System.out.println("直接回车确认使用,也可手动输入自定义地址:");
|
||||
String customUrl = scanner.nextLine().trim();
|
||||
LogUtils.d(TAG, "【参数信息】initAuthCenterCore(),用户输入自定义地址:" + customUrl);
|
||||
|
||||
// 服务器地址合法性校验,自动补全协议头,适配服务器端输入习惯
|
||||
serverUrl = customUrl.isEmpty() ? serverUrl : customUrl;
|
||||
if (!serverUrl.startsWith("http://") && !serverUrl.startsWith("https://")) {
|
||||
System.out.println("⚠️ 服务器地址缺少http/https协议头,自动补充http://前缀");
|
||||
serverUrl = "http://" + serverUrl;
|
||||
LogUtils.w(TAG, "服务器地址格式不合法,自动补全为:" + serverUrl);
|
||||
LogUtils.w(TAG, "【参数修正】initAuthCenterCore(),地址补全后:" + serverUrl);
|
||||
}
|
||||
|
||||
// 初始化服务器交互工具
|
||||
ServerUtils.initServerUrl(serverUrl);
|
||||
// 初始化鉴权核心服务
|
||||
AuthService.init();
|
||||
isServiceRunning = true;
|
||||
|
||||
LogUtils.i(TAG, "鉴权核心组件初始化完成,服务已启动");
|
||||
LogUtils.i(TAG, "【函数结束】initAuthCenterCore(),鉴权核心组件初始化完成");
|
||||
System.out.println("✅ 服务器地址初始化:" + serverUrl);
|
||||
System.out.println("✅ 鉴权服务初始化完成,当前状态:运行中");
|
||||
System.out.println("提示:输入 help 可查看所有支持指令");
|
||||
@@ -67,105 +66,91 @@ public class AuthCenterConsoleApp {
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动控制台交互逻辑,新增生成/校验授权码指令,完善指令体系
|
||||
* 自动获取本机IP 拼接8080端口,带异常兜底
|
||||
*/
|
||||
private static String getLocalHostIpWithPort() {
|
||||
LogUtils.d(TAG, "【函数调用】getLocalHostIpWithPort(),开始获取本机IP拼接端口");
|
||||
String localIp;
|
||||
try {
|
||||
localIp = InetAddress.getLocalHost().getHostAddress();
|
||||
LogUtils.d(TAG, "【参数信息】getLocalHostIpWithPort(),获取本机IP成功:" + localIp);
|
||||
} catch (UnknownHostException e) {
|
||||
LogUtils.e(TAG, "【异常兜底】getLocalHostIpWithPort(),获取本机IP失败,使用127.0.0.1", e);
|
||||
localIp = "127.0.0.1";
|
||||
System.out.println("⚠️ 获取本机IP失败,兜底使用回环地址127.0.0.1");
|
||||
}
|
||||
String serverUrl = "http://" + localIp + ":" + SERVER_PORT;
|
||||
LogUtils.d(TAG, "【函数结束】getLocalHostIpWithPort(),拼接后服务地址:" + serverUrl);
|
||||
return serverUrl;
|
||||
}
|
||||
|
||||
// ========== 控制台交互相关函数 ==========
|
||||
/**
|
||||
* 启动控制台交互逻辑,指令分发处理
|
||||
*/
|
||||
private static void startConsoleInteractive() {
|
||||
LogUtils.d(TAG, "【函数调用】startConsoleInteractive(),启动控制台交互循环");
|
||||
while (true) {
|
||||
System.out.print("AuthCenter > ");
|
||||
String input = scanner.nextLine().trim();
|
||||
if (input.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
// 指令分发,支持大小写忽略
|
||||
switch (input.toLowerCase()) {
|
||||
case "exit":
|
||||
stopAuthService();
|
||||
break;
|
||||
case "start":
|
||||
handleStartService();
|
||||
break;
|
||||
case "status":
|
||||
handleQueryStatus();
|
||||
break;
|
||||
case "gen":
|
||||
handleGenerateAuthCode();
|
||||
break;
|
||||
case "check":
|
||||
handleValidateAuthCode();
|
||||
break;
|
||||
case "help":
|
||||
printHelpInfo();
|
||||
break;
|
||||
default:
|
||||
System.out.println("❌ 无效指令,输入 help 查看所有支持指令");
|
||||
LogUtils.w(TAG, "控制台收到无效指令:" + input);
|
||||
break;
|
||||
}
|
||||
LogUtils.d(TAG, "【参数信息】startConsoleInteractive(),控制台接收指令:" + input);
|
||||
dispatchConsoleCmd(input.toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 指令分发内部函数,精简主函数逻辑
|
||||
*/
|
||||
private static void dispatchConsoleCmd(String cmd) {
|
||||
switch (cmd) {
|
||||
case "exit":
|
||||
stopAuthService();
|
||||
break;
|
||||
case "start":
|
||||
handleStartService();
|
||||
break;
|
||||
case "status":
|
||||
handleQueryStatus();
|
||||
break;
|
||||
case "help":
|
||||
printHelpInfo();
|
||||
break;
|
||||
default:
|
||||
System.out.println("❌ 无效指令,输入 help 查看所有支持指令");
|
||||
LogUtils.w(TAG, "【指令异常】dispatchConsoleCmd(),未识别指令:" + cmd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 指令处理相关函数(按指令名称排序) ==========
|
||||
/**
|
||||
* 处理鉴权服务启动指令
|
||||
*/
|
||||
private static void handleStartService() {
|
||||
if (isServiceRunning) {
|
||||
System.out.println("⚠️ 鉴权服务已处于运行状态,无需重复启动");
|
||||
return;
|
||||
}
|
||||
AuthService.init();
|
||||
isServiceRunning = true;
|
||||
LogUtils.d(TAG, "【函数调用】handleStartService(),执行鉴权服务启动指令");
|
||||
System.out.println("✅ 鉴权服务重新启动成功");
|
||||
LogUtils.i(TAG, "控制台指令触发,鉴权服务重启");
|
||||
LogUtils.i(TAG, "【函数结束】handleStartService(),鉴权服务重启完成");
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理状态查询指令
|
||||
*/
|
||||
private static void handleQueryStatus() {
|
||||
String serviceStatus = isServiceRunning ? "运行中" : "已停止";
|
||||
LogUtils.d(TAG, "【函数调用】handleQueryStatus(),执行服务状态查询指令");
|
||||
String serverUrl = ServerUtils.getServerBaseUrl() == null ? "未初始化" : ServerUtils.getServerBaseUrl();
|
||||
System.out.printf("【当前状态】%n");
|
||||
System.out.printf(" 鉴权服务:%s%n", serviceStatus);
|
||||
System.out.printf(" 服务器地址:%s%n", serverUrl);
|
||||
LogUtils.d(TAG, "控制台执行状态查询,服务状态=" + serviceStatus + ",服务器地址=" + serverUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理授权码生成指令
|
||||
*/
|
||||
private static void handleGenerateAuthCode() {
|
||||
if (!isServiceRunning) {
|
||||
System.out.println("❌ 鉴权服务未启动,无法生成授权码");
|
||||
return;
|
||||
}
|
||||
System.out.println("正在生成授权码...");
|
||||
String authCode = AuthService.getInstance().generateAuthCode();
|
||||
LogUtils.i(TAG, "控制台指令触发授权码生成,结果=" + (authCode == null ? "失败" : "成功"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理授权码校验指令
|
||||
*/
|
||||
private static void handleValidateAuthCode() {
|
||||
if (!isServiceRunning) {
|
||||
System.out.println("❌ 鉴权服务未启动,无法校验授权码");
|
||||
return;
|
||||
}
|
||||
System.out.print("请输入待校验的授权码:");
|
||||
String authCode = scanner.nextLine().trim();
|
||||
if (authCode.isEmpty()) {
|
||||
System.out.println("⚠️ 授权码不能为空,请重新输入");
|
||||
return;
|
||||
}
|
||||
System.out.println("正在校验授权码...");
|
||||
boolean isValid = AuthService.getInstance().validateAuthCode(authCode);
|
||||
LogUtils.i(TAG, "控制台指令触发授权码校验,授权码=" + authCode + ",校验结果=" + isValid);
|
||||
LogUtils.d(TAG, "【函数结束】handleQueryStatus(),状态查询完成,服务器地址:" + serverUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印帮助信息,清晰展示所有支持指令
|
||||
*/
|
||||
private static void printHelpInfo() {
|
||||
LogUtils.d(TAG, "【函数调用】printHelpInfo(),执行打印帮助指令");
|
||||
System.out.println("===== 支持指令列表 =====");
|
||||
System.out.println(" exit - 停止鉴权服务并退出控制台");
|
||||
System.out.println(" start - 启动/重启鉴权服务");
|
||||
@@ -174,22 +159,20 @@ public class AuthCenterConsoleApp {
|
||||
System.out.println(" check - 校验输入的授权码有效性");
|
||||
System.out.println(" help - 查看本帮助信息");
|
||||
System.out.println("========================");
|
||||
LogUtils.d(TAG, "【函数结束】printHelpInfo(),帮助信息打印完成");
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止鉴权服务,释放资源
|
||||
*/
|
||||
private static void stopAuthService() {
|
||||
if (isServiceRunning) {
|
||||
AuthService.stop();
|
||||
isServiceRunning = false;
|
||||
LogUtils.i(TAG, "鉴权服务已停止,资源释放完成");
|
||||
}
|
||||
LogUtils.d(TAG, "【函数调用】stopAuthService(),执行服务停止退出指令");
|
||||
if (scanner != null) {
|
||||
scanner.close();
|
||||
LogUtils.d(TAG, "【资源释放】stopAuthService(),控制台输入流已关闭");
|
||||
}
|
||||
System.out.println("%n===== AuthCenter 鉴权中心控制台已退出 =====");
|
||||
LogUtils.d(TAG, "控制台应用正常退出");
|
||||
LogUtils.i(TAG, "【函数结束】stopAuthService(),控制台应用正常退出");
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package cc.winboll;
|
||||
import cc.winboll.util.MainUtils;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.logging.ConsoleHandler;
|
||||
import java.util.logging.FileHandler;
|
||||
@@ -16,174 +17,174 @@ import java.util.logging.Logger;
|
||||
* 日志工具类,对接Java原生java.util.logging,简化分级调用+统一格式化输出
|
||||
* 适配Java7语法,兼容Android API30,支持异常堆栈打印
|
||||
* 支持通过启动参数动态控制全局日志级别、-v参数控制终端同步输出
|
||||
* 新增:文件日志输出功能,支持指定日志目录存储日志文件
|
||||
* 功能:单文件输出+10MB大小限制+满额时间戳备份+新日志沿用authcenter.log+退出自动清lck锁文件
|
||||
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2026/01/14 00:00:00
|
||||
* @LastEditTime 2026/01/22 21:00:00
|
||||
* @Date 2026-01-14 00:00:00
|
||||
* @LastEditTime 2026-01-24 17:52:00
|
||||
*/
|
||||
public class LogUtils {
|
||||
// 全局日志实例,绑定当前工具类名
|
||||
// ========== 静态常量(常量优先,public对外暴露,private私有) ==========
|
||||
private static final Logger LOGGER = Logger.getLogger(LogUtils.class.getName());
|
||||
// 全局控制台处理器(保留引用,用于动态修改级别/添加移除)
|
||||
private static ConsoleHandler consoleHandler;
|
||||
// 全局文件日志处理器(新增:用于输出日志到指定目录文件)
|
||||
private static FileHandler fileHandler;
|
||||
// 日志文件命名前缀(可按需调整)
|
||||
private static final String LOG_FILE_PREFIX = "authcenter_";
|
||||
// 日志文件后缀
|
||||
private static final String LOG_FILE_SUFFIX = ".log";
|
||||
public static final String LOG_FILE_PREFIX = "authcenter_";
|
||||
public static final String LOG_FILE_SUFFIX = ".log";
|
||||
private static final int MAX_LOG_SIZE = 10 * 1024 * 1024;
|
||||
private static final SimpleDateFormat BACKUP_SDF = new SimpleDateFormat("yyyyMMddHHmmss");
|
||||
|
||||
// 静态代码块初始化日志配置(仅执行一次,全局生效)
|
||||
// ========== 静态成员变量 ==========
|
||||
private static ConsoleHandler consoleHandler;
|
||||
private static FileHandler fileHandler;
|
||||
|
||||
// ========== 静态初始化块(全局仅执行一次) ==========
|
||||
static {
|
||||
// 关闭父处理器,避免日志重复输出
|
||||
LOGGER.setUseParentHandlers(false);
|
||||
// 初始化控制台处理器,但暂不添加到Logger,由-v参数控制
|
||||
consoleHandler = new ConsoleHandler();
|
||||
consoleHandler.setFormatter(new CustomLogFormatter());
|
||||
// 默认日志级别(全量输出,可通过启动参数覆盖)
|
||||
setGlobalLogLevel(Level.ALL);
|
||||
LogUtils.d("LogUtils", "静态初始化完成,默认日志级别ALL,单文件10MB限制+时间戳备份+lck自动清理");
|
||||
}
|
||||
|
||||
// 私有化构造方法,禁止外部实例化
|
||||
// ========== 私有化构造器,禁止外部实例化 ==========
|
||||
private LogUtils() {}
|
||||
|
||||
/**
|
||||
* 工具方法:判断Logger是否包含指定Handler(Java7兼容)
|
||||
* @param handler 目标Handler
|
||||
* @return 包含返回true,否则返回false
|
||||
*/
|
||||
// ========== 私有工具方法(内部调用,按功能归类) ==========
|
||||
private static boolean containsHandler(Handler handler) {
|
||||
LogUtils.d("LogUtils", "【函数调用】containsHandler,入参handler=" + (handler == null ? "null" : handler.getClass().getName()));
|
||||
if (handler == null) {
|
||||
return false;
|
||||
}
|
||||
Handler[] handlers = LOGGER.getHandlers();
|
||||
for (Handler h : handlers) {
|
||||
if (h == handler) { // 引用比较,确保是同一个处理器实例
|
||||
for (Handler h : LOGGER.getHandlers()) {
|
||||
if (h == handler) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 【新增缺失方法】设置日志输出目录,并初始化文件日志处理器
|
||||
* 支持Android/sdcard路径,自动创建不存在的目录
|
||||
* @param logDir 日志文件存储的目录路径
|
||||
*/
|
||||
private static void backupOldLog(File logFile) {
|
||||
LogUtils.d("LogUtils", "【函数调用】backupOldLog,入参logFile=" + (logFile == null ? "null" : logFile.getAbsolutePath()));
|
||||
if (logFile != null && logFile.exists() && logFile.length() >= MAX_LOG_SIZE) {
|
||||
String backupName = LOG_FILE_PREFIX + BACKUP_SDF.format(new Date()) + LOG_FILE_SUFFIX;
|
||||
File backupFile = new File(logFile.getParent(), backupName);
|
||||
boolean backupSuccess = logFile.renameTo(backupFile);
|
||||
if (backupSuccess) {
|
||||
LogUtils.i("LogUtils", "旧日志备份成功,备份文件:" + backupFile.getAbsolutePath());
|
||||
} else {
|
||||
LogUtils.w("LogUtils", "旧日志备份失败,将直接覆盖原日志");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 公共核心方法(对外提供功能,核心方法优先) ==========
|
||||
public static void setLogDir(String logDir) {
|
||||
LogUtils.d("LogUtils", "setLogDir 函数调用,指定日志目录:" + logDir);
|
||||
// 1. 校验目录路径
|
||||
LogUtils.d("LogUtils", "【函数调用】setLogDir,入参logDir=" + logDir);
|
||||
if (logDir == null || logDir.trim().isEmpty()) {
|
||||
LogUtils.w("LogUtils", "日志目录路径为空,跳过文件处理器初始化");
|
||||
LogUtils.w("LogUtils", "日志目录为空,跳过文件处理器初始化");
|
||||
return;
|
||||
}
|
||||
File dir = new File(logDir);
|
||||
// 2. 自动创建目录(包括多级目录)
|
||||
File dir = new File(logDir.trim());
|
||||
if (!dir.exists()) {
|
||||
boolean mkdirSuccess = dir.mkdirs();
|
||||
if (mkdirSuccess) {
|
||||
LogUtils.i("LogUtils", "日志目录创建成功:" + logDir);
|
||||
boolean success = dir.mkdirs();
|
||||
if (success) {
|
||||
LogUtils.i("LogUtils", "日志目录创建成功:" + dir.getAbsolutePath());
|
||||
} else {
|
||||
LogUtils.e("LogUtils", "日志目录创建失败:" + logDir);
|
||||
LogUtils.e("LogUtils", "日志目录创建失败:" + dir.getAbsolutePath());
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 3. 初始化文件日志处理器
|
||||
|
||||
try {
|
||||
// 配置:日志文件追加模式 + 自定义格式化
|
||||
String logFilePath = new File(dir, LOG_FILE_PREFIX).getAbsolutePath();
|
||||
fileHandler = new FileHandler(logFilePath + "%u" + LOG_FILE_SUFFIX, true);
|
||||
final File logFile = new File(dir, "authcenter.log");
|
||||
backupOldLog(logFile);
|
||||
|
||||
fileHandler = new FileHandler(logFile.getAbsolutePath(), MAX_LOG_SIZE, 1, true);
|
||||
fileHandler.setFormatter(new CustomLogFormatter());
|
||||
fileHandler.setLevel(Level.ALL); // 文件日志默认输出全级别
|
||||
// 4. 添加文件处理器到Logger(避免重复添加)
|
||||
fileHandler.setLevel(Level.ALL);
|
||||
if (!containsHandler(fileHandler)) {
|
||||
LOGGER.addHandler(fileHandler);
|
||||
LogUtils.i("LogUtils", "文件日志处理器初始化成功,日志将输出到:" + logDir);
|
||||
LogUtils.i("LogUtils", "日志处理器初始化成功,当前日志:" + logFile.getAbsolutePath() + ",最大10MB");
|
||||
LogUtils.i("LogUtils", "满额自动备份为authcenter_时间戳.log,新日志沿用authcenter.log");
|
||||
}
|
||||
// JVM关闭钩子,退出强制清理lck锁文件
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
new File(logFile.getAbsolutePath() + ".lck").delete();
|
||||
}
|
||||
}));
|
||||
LogUtils.d("LogUtils", "lck文件清理钩子已注册,程序退出自动清理");
|
||||
} catch (IOException e) {
|
||||
LogUtils.e("LogUtils", "初始化文件日志处理器失败", e);
|
||||
LogUtils.e("LogUtils", "文件日志处理器初始化失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 【新增】获取当前全局日志级别,供外部工具类调用
|
||||
* @return 当前生效的全局日志级别对象
|
||||
*/
|
||||
public static Level getGlobalLogLevel() {
|
||||
LogUtils.d("LogUtils", "【函数调用】getGlobalLogLevel,返回当前级别:" + LOGGER.getLevel().getName());
|
||||
return LOGGER.getLevel();
|
||||
}
|
||||
|
||||
/**
|
||||
* 动态设置全局日志级别 + 控制终端输出开关(-v参数驱动)
|
||||
* @param level 目标日志级别(ALL/FINE/INFO/WARNING/SEVERE)
|
||||
*/
|
||||
public static void setGlobalLogLevel(Level level) {
|
||||
LogUtils.d("LogUtils", "【函数调用】setGlobalLogLevel,入参level=" + (level == null ? "null" : level.getName()));
|
||||
if (level == null) {
|
||||
LogUtils.w("LogUtils", "日志级别为null,不执行设置");
|
||||
return;
|
||||
}
|
||||
LOGGER.setLevel(level);
|
||||
// 设置控制台处理器级别
|
||||
if (consoleHandler != null) {
|
||||
consoleHandler.setLevel(level);
|
||||
}
|
||||
// 设置文件处理器级别(新增:同步级别配置)
|
||||
if (fileHandler != null) {
|
||||
fileHandler.setLevel(level);
|
||||
}
|
||||
// 根据 MainUtils 中 -v 参数状态,动态控制控制台输出
|
||||
if (MainUtils.isVerbose()) {
|
||||
if (!containsHandler(consoleHandler)) {
|
||||
LOGGER.addHandler(consoleHandler);
|
||||
LogUtils.d("LogUtils", "终端日志输出已开启(-v参数生效)");
|
||||
}
|
||||
} else {
|
||||
if (containsHandler(consoleHandler)) {
|
||||
LOGGER.removeHandler(consoleHandler);
|
||||
LogUtils.d("LogUtils", "终端日志输出已关闭(-v参数未生效)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 调试日志(细粒度开发调试信息,上线可屏蔽)
|
||||
// ========== 分级日志方法(重载方法归类,简洁无冗余) ==========
|
||||
public static void d(String tag, String msg) {
|
||||
LOGGER.fine(String.format("[%s] %s", tag, msg));
|
||||
}
|
||||
|
||||
// 调试日志-重载版(带异常堆栈,便于调试阶段排查问题)
|
||||
public static void d(String tag, String msg, Throwable throwable) {
|
||||
LOGGER.log(Level.FINE, String.format("[%s] %s", tag, msg), throwable);
|
||||
}
|
||||
|
||||
// 信息日志(常规运行状态、流程节点信息)
|
||||
public static void i(String tag, String msg) {
|
||||
LOGGER.info(String.format("[%s] %s", tag, msg));
|
||||
}
|
||||
|
||||
// 信息日志-重载版(带异常堆栈,关联信息类场景的异常链路)
|
||||
public static void i(String tag, String msg, Throwable throwable) {
|
||||
LOGGER.log(Level.INFO, String.format("[%s] %s", tag, msg), throwable);
|
||||
}
|
||||
|
||||
// 警告日志-基础版(非致命异常、潜在风险提示)
|
||||
public static void w(String tag, String msg) {
|
||||
LOGGER.warning(String.format("[%s] %s", tag, msg));
|
||||
}
|
||||
|
||||
// 警告日志-重载版(适配异常场景调用)
|
||||
public static void w(String tag, String msg, Throwable throwable) {
|
||||
LOGGER.log(Level.WARNING, String.format("[%s] %s", tag, msg), throwable);
|
||||
}
|
||||
|
||||
// 错误日志-基础版(致命异常、核心流程错误提示)
|
||||
public static void e(String tag, String msg) {
|
||||
LOGGER.severe(String.format("[%s] %s", tag, msg));
|
||||
}
|
||||
|
||||
// 错误日志-重载版(核心补充,支持异常堆栈打印)
|
||||
public static void e(String tag, String msg, Throwable throwable) {
|
||||
LOGGER.log(Level.SEVERE, String.format("[%s] %s", tag, msg), throwable);
|
||||
}
|
||||
|
||||
// 自定义日志格式化器,统一输出格式,提升可读性
|
||||
// ========== 内部类:自定义日志格式化器 ==========
|
||||
static class CustomLogFormatter extends Formatter {
|
||||
@Override
|
||||
public String format(LogRecord record) {
|
||||
// 输出格式:[时间戳] [日志级别] [线程名] [日志源] - 日志内容
|
||||
return String.format("[%1$tF %1$tT] [%2$s] [%3$s] %4$s - %5$s%n",
|
||||
new Date(record.getMillis()),
|
||||
record.getLevel().getName(),
|
||||
|
||||
@@ -23,50 +23,61 @@ import java.util.logging.Level;
|
||||
* 4. -log:LEVEL(可选,指定日志级别,支持ALL/FINE/INFO/WARNING/SEVERE)
|
||||
* 支持控制台输入交互,输入help查帮助、输入exit退出、其他指令提示未识别
|
||||
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2026/01/14 00:00:00
|
||||
* @LastEditTime 2026/01/23 15:00:00
|
||||
* @Date 2026-01-14 00:00:00
|
||||
* @LastEditTime 2026-01-26 17:18:36
|
||||
*/
|
||||
public class Main {
|
||||
private static final String TAG = "AuthCenterMain";
|
||||
// ========== 静态属性(常量在前 变量在后,顺序规整) ==========
|
||||
private static final String TAG = "Main";
|
||||
private static final String CONFIG_SECTION_GLOBAL = "GlobalConfig";
|
||||
private static final String CONFIG_KEY_ROOT_DIR = "PROJECT_ROOT_DIR";
|
||||
private static AuthCenterHttpService httpService;
|
||||
private static int httpPort;
|
||||
// 核心状态:volatile保证多线程可见性,false=运行,true=退出
|
||||
private static volatile boolean isExit = false;
|
||||
// 固定安卓兜底根目录
|
||||
public static final String DEFAULT_ROOT = "/sdcard/WinBoLLStudio/Sources/AuthCenterConsoleApp";
|
||||
|
||||
// ========== 程序入口方法 ==========
|
||||
public static void main(String[] args) {
|
||||
// 1. 初始化退出状态为false,表示程序正常运行
|
||||
LogUtils.d(TAG, "【函数调用】main(),AuthCenter程序启动,开始执行初始化流程");
|
||||
isExit = false;
|
||||
|
||||
// 2. 优先解析所有启动参数到MainUtils的argMap,全局唯一入口
|
||||
// 解析启动参数 + 加载INI根配置
|
||||
MainUtils.parseArgs(args);
|
||||
// 3. 加载INI配置
|
||||
IniConfigUtils.loadRootConfig();
|
||||
LogUtils.d(TAG, "【参数信息】main(),传入启动参数:" + MainUtils.arrayToString(args));
|
||||
if (!IniConfigUtils.loadRootConfig()) {
|
||||
LogUtils.e(TAG, "INI配置文件加载失败,程序无法启动,强制退出");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
// 4. 从argMap获取日志级别并设置,确保后续日志输出符合预期
|
||||
// 读取项目根目录 初始化日志目录
|
||||
String projectRootDir = IniConfigUtils.getConfigValue(CONFIG_SECTION_GLOBAL, CONFIG_KEY_ROOT_DIR);
|
||||
if (projectRootDir == null || projectRootDir.trim().isEmpty()) {
|
||||
LogUtils.e(TAG, "INI中GlobalConfig.PROJECT_ROOT_DIR未配置,程序退出");
|
||||
System.err.println("❌ 请在INI配置GlobalConfig.PROJECT_ROOT_DIR");
|
||||
System.exit(1);
|
||||
}
|
||||
String logDir = projectRootDir.trim().replaceAll("/$", "") + "/logs";
|
||||
LogUtils.setLogDir(logDir);
|
||||
|
||||
// 设置全局日志级别
|
||||
Level logLevel = MainUtils.parseLogLevelArg();
|
||||
LogUtils.setGlobalLogLevel(logLevel);
|
||||
LogUtils.i(TAG, "全局日志级别已设置:" + logLevel.getName());
|
||||
LogUtils.i(TAG, "日志目录已配置,路径:" + logDir);
|
||||
|
||||
// 5. 输出启动参数与日志级别信息,此时日志级别已生效
|
||||
LogUtils.d(TAG, "【函数调用】main(),传入参数:" + MainUtils.arrayToString(args));
|
||||
LogUtils.i(TAG, "当前全局日志级别已设置为:" + logLevel.getName());
|
||||
// 打印-v参数解析状态,验证是否生效
|
||||
// 获取解析后各项参数
|
||||
boolean isVerbose = MainUtils.isVerbose();
|
||||
LogUtils.i(TAG, "终端日志同步输出状态:" + (isVerbose ? "✅ 开启" : "❌ 关闭"));
|
||||
|
||||
// 6. 从argMap读取参数,不再传递args
|
||||
boolean showDetailEnv = MainUtils.parseDetailArg();
|
||||
String serverUrl = MainUtils.getFinalServerUrl();
|
||||
httpPort = MainUtils.getHttpPortFromConfig();
|
||||
LogUtils.i(TAG, "终端日志同步输出状态:" + (isVerbose ? "✅ 开启" : "❌ 关闭"));
|
||||
LogUtils.d(TAG, "【参数信息】main(),解析后服务器地址:" + serverUrl + ",服务端口:" + httpPort);
|
||||
|
||||
// 7. 环境信息打印与工具类初始化
|
||||
// 环境信息打印 + 工具类初始化
|
||||
EnvInfoUtils.printEnvReport(showDetailEnv);
|
||||
// 移除冗余校验:MainUtils.getFinalServerUrl 已实现非法地址过滤
|
||||
initServerUtils(serverUrl);
|
||||
EmailSendUtils.initEmailConfig();
|
||||
|
||||
// 8. 初始化校验,不通过则退出
|
||||
// 启动配置校验
|
||||
boolean initCheckPass = InitCheckUtils.checkAllInitConfig();
|
||||
if (!initCheckPass) {
|
||||
LogUtils.e(TAG, "初始化校验未通过,程序退出");
|
||||
@@ -76,46 +87,44 @@ public class Main {
|
||||
|
||||
System.out.println("Hello World!");
|
||||
|
||||
// 9. 启动HTTP服务 + 初始化控制台输入
|
||||
// 启动HTTP服务 + 初始化控制台
|
||||
startAuthCenterHttpService();
|
||||
ConsoleInputUtils.initConsoleScanner();
|
||||
|
||||
// 服务启动成功后发送通知邮件
|
||||
AdminUtils.getInstance().sendServiceStartNotify();
|
||||
LogUtils.i(TAG, "HTTP服务+控制台初始化完成,服务进入常驻状态");
|
||||
|
||||
// 阻塞等待指令:修复循环逻辑,持续监听控制台输入
|
||||
// 控制台阻塞监听
|
||||
consoleBlockForService();
|
||||
|
||||
// 10. 资源释放与程序退出:新增扫描器关闭,保证资源完整回收
|
||||
// 正常退出 释放资源
|
||||
LogUtils.d(TAG, "【函数执行】main(),开始执行资源释放与停机流程");
|
||||
ConsoleInputUtils.closeConsoleScanner();
|
||||
// 兜底发送停止通知,覆盖控制台exit指令场景
|
||||
AdminUtils.getInstance().sendServiceStopNotify();
|
||||
MainUtils.releaseAllResources(httpService);
|
||||
LogUtils.i(TAG, "【函数结束】main(),程序正常退出");
|
||||
LogUtils.i(TAG, "【函数结束】main(),AuthCenter程序正常退出");
|
||||
}
|
||||
|
||||
// ========== 私有工具方法 ==========
|
||||
/**
|
||||
* 初始化服务器交互工具类
|
||||
*/
|
||||
private static void initServerUtils(String serverUrl) {
|
||||
LogUtils.d(TAG, "【函数调用】initServerUtils(),入参初始服务器地址:" + serverUrl);
|
||||
LogUtils.d(TAG, "【函数调用】initServerUtils(),开始初始化服务器工具类");
|
||||
LogUtils.d(TAG, "【参数信息】initServerUtils(),原始服务器地址:" + serverUrl);
|
||||
String finalServerUrl = MainUtils.completeServerUrlProtocol(serverUrl);
|
||||
|
||||
ServerUtils.initServerUrl(finalServerUrl);
|
||||
System.out.println("✅ ServerUtils 初始化完成,当前服务器地址:" + finalServerUrl);
|
||||
LogUtils.d(TAG, "【函数结束】initServerUtils(),服务器交互工具初始化就绪");
|
||||
LogUtils.d(TAG, "【函数结束】initServerUtils(),服务器工具类初始化就绪");
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化并启动AuthCenter HTTP服务
|
||||
* 启动AuthCenter HTTP服务,绑定控制台重启实例
|
||||
*/
|
||||
private static void startAuthCenterHttpService() {
|
||||
LogUtils.d(TAG, "【函数调用】startAuthCenterHttpService(),开始启动HTTP服务");
|
||||
LogUtils.d(TAG, "【函数调用】startAuthCenterHttpService(),准备启动HTTP服务,端口:" + httpPort);
|
||||
httpService = new AuthCenterHttpService(httpPort);
|
||||
|
||||
try {
|
||||
httpService.start();
|
||||
// 绑定HTTP服务实例到控制台工具类,支持restart指令
|
||||
ConsoleInputUtils.setHttpService(httpService);
|
||||
System.out.println("\nAuthCenterHttpService 启动成功,监听端口:" + httpPort);
|
||||
System.out.println("可访问接口:GET http://localhost:" + httpPort + "/authcenter/ping");
|
||||
@@ -124,7 +133,6 @@ public class Main {
|
||||
String errMsg = "HTTP服务启动失败!端口 " + httpPort + " 可能被占用";
|
||||
System.err.println(errMsg);
|
||||
LogUtils.e(TAG, "【函数异常】startAuthCenterHttpService(),HTTP服务启动失败", e);
|
||||
// 启动失败时标记为需要退出
|
||||
isExit = true;
|
||||
ConsoleInputUtils.forceExit();
|
||||
ConsoleInputUtils.closeConsoleScanner();
|
||||
@@ -134,54 +142,45 @@ public class Main {
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务常驻阻塞逻辑(优化:解决CPU高占用+确保指令通知执行)
|
||||
* 服务常驻阻塞逻辑,低CPU占用,支持优雅退出
|
||||
*/
|
||||
private static void consoleBlockForService() {
|
||||
LogUtils.d(TAG, "【函数调用】consoleBlockForService(),开始注册关闭钩子与常驻阻塞");
|
||||
// 注册JVM关闭钩子:仅处理外部kill/Ctrl+C信号的兜底停机
|
||||
LogUtils.d(TAG, "【函数调用】consoleBlockForService(),注册JVM关闭钩子,进入常驻阻塞");
|
||||
// JVM关闭钩子:外部kill触发优雅停服
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
LogUtils.i(TAG, "收到JVM停止信号,执行兜底停机流程");
|
||||
isExit = true;
|
||||
ConsoleInputUtils.forceExit();
|
||||
if (httpService != null && httpService.isRunning()) {
|
||||
httpService.stop();
|
||||
LogUtils.i(TAG, "HTTP服务已通过关闭钩子停止");
|
||||
}
|
||||
}
|
||||
}));
|
||||
@Override
|
||||
public void run() {
|
||||
LogUtils.i(TAG, "收到JVM停止信号,执行服务优雅停止流程");
|
||||
ConsoleInputUtils.handleExitCmd();
|
||||
}
|
||||
}, "GracefulStop-Hook"));
|
||||
|
||||
System.out.println("\n服务已常驻运行,输入exit可退出服务,或执行kill 对应Java进程号停机");
|
||||
// 核心优化:利用scanner.hasNextLine()阻塞特性,避免CPU空转
|
||||
while (!isExit) {
|
||||
try {
|
||||
boolean needExit = ConsoleInputUtils.checkConsoleCommand();
|
||||
if (needExit) {
|
||||
isExit = true;
|
||||
LogUtils.i(TAG, "检测到退出指令,触发服务停机流程");
|
||||
LogUtils.i(TAG, "检测到exit指令,触发服务正常停机流程");
|
||||
System.exit(0);
|
||||
break;
|
||||
}
|
||||
// 兼容特殊终端环境,降低CPU占用
|
||||
Thread.sleep(50);
|
||||
} catch (InterruptedException e) {
|
||||
LogUtils.w(TAG, "控制台监听线程被中断", e);
|
||||
LogUtils.w(TAG, "控制台监听线程被中断,触发退出");
|
||||
isExit = true;
|
||||
LogUtils.e(TAG, "【退出详情】监听线程中断,退出状态码:200(线程异常中断)");
|
||||
System.exit(200);
|
||||
}
|
||||
}
|
||||
LogUtils.d(TAG, "【函数结束】consoleBlockForService(),常驻阻塞流程退出");
|
||||
}
|
||||
|
||||
/**
|
||||
* 对外暴露退出状态查询方法(供其他工具类调用)
|
||||
*/
|
||||
// ========== 对外暴露方法 ==========
|
||||
public static boolean isExit() {
|
||||
return isExit;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对外暴露退出状态设置方法(供ConsoleInputUtils等工具类调用)
|
||||
*/
|
||||
public static void setExit(boolean exit) {
|
||||
isExit = exit;
|
||||
}
|
||||
|
||||
@@ -1,190 +0,0 @@
|
||||
package cc.winboll.service;
|
||||
|
||||
import cc.winboll.LogUtils;
|
||||
import cc.winboll.config.AuthConfig;
|
||||
import cc.winboll.util.AuthUtils;
|
||||
|
||||
/**
|
||||
* 鉴权核心服务类,单例模式保证全局唯一实例,统一管理鉴权环境与核心功能
|
||||
* 提供授权码生成、校验、服务启停能力,适配控制台与应用端调用
|
||||
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2026/01/15 18:40:00
|
||||
* @LastEditTime 2026/01/15 19:10:00
|
||||
*/
|
||||
public class AuthService {
|
||||
private static final String TAG = "AuthService";
|
||||
// 单例实例(饿汉式,线程安全,适配Java7,无需复杂同步)
|
||||
private static volatile AuthService instance;
|
||||
// 服务运行状态标识
|
||||
private boolean isServiceInitialized = false;
|
||||
|
||||
/**
|
||||
* 私有构造方法,禁止外部实例化,保障单例唯一性
|
||||
*/
|
||||
private AuthService() {}
|
||||
|
||||
/**
|
||||
* 获取单例实例(双重校验锁,兼顾线程安全与性能)
|
||||
*/
|
||||
public static AuthService getInstance() {
|
||||
if (instance == null) {
|
||||
synchronized (AuthService.class) {
|
||||
if (instance == null) {
|
||||
instance = new AuthService();
|
||||
LogUtils.d(TAG, "AuthService 单例实例创建完成");
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 静态初始化入口(适配控制台全局调用,简化使用)
|
||||
*/
|
||||
public static void init() {
|
||||
AuthService service = getInstance();
|
||||
if (!service.isServiceInitialized) {
|
||||
service.initAuthEnvironment();
|
||||
service.isServiceInitialized = true;
|
||||
LogUtils.i(TAG, "AuthService 静态初始化完成,服务已就绪");
|
||||
} else {
|
||||
LogUtils.w(TAG, "AuthService 已初始化,无需重复执行");
|
||||
System.out.println("⚠️ 鉴权服务已初始化,请勿重复操作");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 静态停止入口(适配控制台资源释放,闭环服务生命周期)
|
||||
*/
|
||||
public static void stop() {
|
||||
AuthService service = getInstance();
|
||||
if (service.isServiceInitialized) {
|
||||
service.destroyAuthEnvironment();
|
||||
service.isServiceInitialized = false;
|
||||
LogUtils.i(TAG, "AuthService 已停止,资源释放完成");
|
||||
} else {
|
||||
LogUtils.w(TAG, "AuthService 未初始化,无需执行停止操作");
|
||||
System.out.println("⚠️ 鉴权服务未启动,无需停止");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化授权运行环境(核心初始化逻辑,仅执行一次)
|
||||
*/
|
||||
public void initAuthEnvironment() {
|
||||
if (isServiceInitialized) {
|
||||
System.out.println("⚠️ 授权环境已初始化,无需重复执行");
|
||||
return;
|
||||
}
|
||||
System.out.println("1. 授权环境初始化中...");
|
||||
LogUtils.d(TAG, "开始初始化授权运行环境,加载配置参数");
|
||||
|
||||
// 校验核心配置有效性,避免空配置导致后续异常
|
||||
if (AuthConfig.AUTH_EXPIRE_SECONDS <= 0) {
|
||||
LogUtils.w(TAG, "授权有效期配置异常,当前值:" + AuthConfig.AUTH_EXPIRE_SECONDS + "s,建议设置大于300s");
|
||||
System.out.println(" ⚠ 注意:授权有效期配置异常,需检查 AuthConfig 配置项");
|
||||
}
|
||||
if (AuthUtils.isEmpty(AuthConfig.AUTH_SECRET_PREFIX)) {
|
||||
LogUtils.w(TAG, "密钥前缀配置为空,可能影响授权码安全性");
|
||||
System.out.println(" ⚠ 注意:密钥前缀配置为空,建议补充 AuthConfig 配置");
|
||||
}
|
||||
|
||||
// 打印配置信息,可视化初始化结果
|
||||
System.out.println(" - 加载授权配置: 有效期 " + AuthConfig.AUTH_EXPIRE_SECONDS + "s");
|
||||
System.out.println(" - 加载密钥前缀: " + (AuthUtils.isEmpty(AuthConfig.AUTH_SECRET_PREFIX) ? "未配置" : AuthConfig.AUTH_SECRET_PREFIX));
|
||||
System.out.println(" - 环境初始化完成 ✅");
|
||||
LogUtils.i(TAG, "授权运行环境初始化完成,配置参数加载成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁授权环境,释放资源(服务停止时执行,保障资源闭环)
|
||||
*/
|
||||
private void destroyAuthEnvironment() {
|
||||
System.out.println("1. 授权环境销毁中...");
|
||||
LogUtils.d(TAG, "开始销毁授权运行环境,释放相关资源");
|
||||
// 可扩展:后续添加密钥清空、缓存释放等资源回收逻辑
|
||||
System.out.println(" - 已释放授权相关资源");
|
||||
System.out.println(" - 环境销毁完成 ✅");
|
||||
LogUtils.i(TAG, "授权运行环境销毁完成");
|
||||
}
|
||||
|
||||
/**
|
||||
* 授权码核心校验逻辑(带日志增强,便于问题定位)
|
||||
* @param authCode 待校验授权码
|
||||
* @return 校验结果
|
||||
*/
|
||||
public boolean validateAuthCode(String authCode) {
|
||||
if (!isServiceInitialized) {
|
||||
LogUtils.e(TAG, "授权码校验失败:鉴权服务未初始化");
|
||||
System.out.println("❌ 授权码校验失败:鉴权服务未初始化,请先执行init()");
|
||||
return false;
|
||||
}
|
||||
|
||||
LogUtils.d(TAG, "开始校验授权码,入参 authCode=" + (AuthUtils.isEmpty(authCode) ? "空" : authCode));
|
||||
// 1. 非空校验
|
||||
if (AuthUtils.isEmpty(authCode)) {
|
||||
LogUtils.w(TAG, "授权码校验失败:授权码不能为空");
|
||||
System.out.println("校验失败原因: 授权码不能为空");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. 格式校验(匹配特征标识)
|
||||
if (!authCode.startsWith(AuthConfig.VALID_AUTH_CODE_FLAG)) {
|
||||
LogUtils.w(TAG, "授权码校验失败:格式不合法,缺少特征标识,当前标识=" + AuthConfig.VALID_AUTH_CODE_FLAG);
|
||||
System.out.println("校验失败原因: 授权码格式不合法,缺少特征标识");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. 签名校验(核心安全校验)
|
||||
String sign = AuthUtils.generateAuthSign(authCode);
|
||||
boolean signValid = AuthUtils.validateAuthSign(authCode, sign);
|
||||
if (!signValid) {
|
||||
LogUtils.w(TAG, "授权码校验失败:签名验证不通过,生成签名=" + sign);
|
||||
System.out.println("校验失败原因: 授权码签名验证不通过");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4. 有效期校验(保障授权时效性)
|
||||
boolean notExpired = AuthUtils.checkAuthNotExpired(authCode);
|
||||
if (!notExpired) {
|
||||
LogUtils.w(TAG, "授权码校验失败:授权码已过期");
|
||||
System.out.println("校验失败原因: 授权码已过期");
|
||||
return false;
|
||||
}
|
||||
|
||||
LogUtils.i(TAG, "授权码校验通过,authCode=" + authCode);
|
||||
System.out.println("✅ 授权码校验通过");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成合法授权码(对外提供接口,带状态校验)
|
||||
* @return 合法授权码
|
||||
*/
|
||||
public String generateAuthCode() {
|
||||
if (!isServiceInitialized) {
|
||||
LogUtils.e(TAG, "生成授权码失败:鉴权服务未初始化");
|
||||
System.out.println("❌ 生成授权码失败:鉴权服务未初始化,请先执行init()");
|
||||
return null;
|
||||
}
|
||||
|
||||
LogUtils.d(TAG, "开始生成合法授权码");
|
||||
String authCode = AuthUtils.generateValidAuthCode();
|
||||
if (!AuthUtils.isEmpty(authCode)) {
|
||||
LogUtils.i(TAG, "生成合法授权码成功: " + authCode);
|
||||
System.out.println("生成合法授权码成功: " + authCode);
|
||||
} else {
|
||||
LogUtils.e(TAG, "生成授权码失败:AuthUtils 返回空授权码");
|
||||
System.out.println("❌ 生成授权码失败,请检查工具类配置");
|
||||
}
|
||||
return authCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取服务初始化状态(对外提供状态查询入口)
|
||||
*/
|
||||
public boolean isServiceInitialized() {
|
||||
return isServiceInitialized;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package cc.winboll.test;
|
||||
|
||||
import cc.winboll.Main;
|
||||
import cc.winboll.LogUtils;
|
||||
import cc.winboll.auth.MailAuthUtils;
|
||||
import cc.winboll.util.ConsoleInputUtils;
|
||||
@@ -23,16 +22,16 @@ import java.util.logging.Level;
|
||||
* AuthCenter 控制台指令自动化测试类
|
||||
* 适配 Java7 语言规范与 Android API30 运行环境
|
||||
* 自动执行控制台指令测试用例,生成结构化测试报告
|
||||
* 核心优化:从config.ini读取配置,动态初始化测试路径与参数,兜底根目录固定为安卓存储路径
|
||||
* 核心优化:从config.ini读取配置,动态初始化测试路径与参数,根目录配置缺失直接日志退出
|
||||
* 调整:邮件测试用例改为调用 EmailSendUtils.test() 函数,利用自动初始化特性
|
||||
* 新增:loadExternalConfig配置加载成功后输出提示信息
|
||||
* 修复:移除重复配置加载,消除矛盾日志输出
|
||||
* 修复:移除重复配置加载,消除矛盾日志输出;移除Main.DEFAULT_ROOT依赖
|
||||
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2026/01/20 10:00:00
|
||||
* @LastEditTime 2026/01/21 19:00:00
|
||||
* @LastEditTime 2026/01/24 01:40:00
|
||||
*/
|
||||
public class ConsoleCmdAutoTest {
|
||||
// ========== 静态常量(配置键名+兜底根目录,不可变) ==========
|
||||
// ========== 静态常量(配置键名,不可变) ==========
|
||||
private static final String TAG = "ConsoleCmdAutoTest";
|
||||
private static final String CONFIG_KEY_ROOT_DIR = "PROJECT_ROOT_DIR";
|
||||
private static final String CONFIG_KEY_TEST_CASE_DIR = "test_case_dir";
|
||||
@@ -73,7 +72,7 @@ public class ConsoleCmdAutoTest {
|
||||
public static void main(String[] args) {
|
||||
LogUtils.d(TAG, "【函数调用】main(),自动化测试程序启动");
|
||||
|
||||
// 1. 加载外部config.ini(优先从启动参数获取配置路径,兜底用默认路径)
|
||||
// 1. 加载外部config.ini,配置缺失直接退出
|
||||
loadExternalConfig(args);
|
||||
|
||||
// 2. 执行测试流程
|
||||
@@ -95,7 +94,7 @@ public class ConsoleCmdAutoTest {
|
||||
/**
|
||||
* 从启动参数读取config.ini路径,无参数则使用当前目录下的config.ini
|
||||
* 启动参数格式:-config:xxx/config.ini
|
||||
* 新增:配置加载成功后输出日志与控制台提示
|
||||
* 核心调整:PROJECT_ROOT_DIR缺失直接日志退出,移除兜底路径
|
||||
*/
|
||||
private static void loadExternalConfig(String[] args) {
|
||||
LogUtils.d(TAG, "【函数调用】loadExternalConfig(),加载外部配置文件");
|
||||
@@ -118,15 +117,15 @@ public class ConsoleCmdAutoTest {
|
||||
// 加载配置文件,读取核心路径参数
|
||||
try {
|
||||
IniConfigUtils.loadConfig(CONFIG_FILE_PATH);
|
||||
// ========== 配置加载成功提示 ==========
|
||||
LogUtils.i(TAG, "【配置加载成功】已成功读取配置文件:" + CONFIG_FILE_PATH);
|
||||
System.out.println("✅ 配置文件加载成功:" + CONFIG_FILE_PATH);
|
||||
|
||||
// 读取项目根目录(必填),配置缺失时使用固定安卓兜底路径
|
||||
// 读取项目根目录(必填),缺失直接日志退出
|
||||
PROJECT_ROOT_DIR = IniConfigUtils.getConfigValue(CONFIG_SECTION_GLOBAL, CONFIG_KEY_ROOT_DIR);
|
||||
if (PROJECT_ROOT_DIR == null || PROJECT_ROOT_DIR.trim().isEmpty()) {
|
||||
PROJECT_ROOT_DIR = Main.DEFAULT_ROOT;
|
||||
LogUtils.w(TAG, "【配置缺失】未找到PROJECT_ROOT_DIR,使用安卓兜底根目录:" + PROJECT_ROOT_DIR);
|
||||
LogUtils.e(TAG, "【配置缺失】GlobalConfig小节必填项PROJECT_ROOT_DIR未配置,程序退出");
|
||||
System.err.println("❌ 配置缺失:GlobalConfig.PROJECT_ROOT_DIR 不能为空,请检查config.ini");
|
||||
System.exit(1);
|
||||
}
|
||||
PROJECT_ROOT_DIR = PROJECT_ROOT_DIR.trim();
|
||||
|
||||
@@ -145,13 +144,9 @@ public class ConsoleCmdAutoTest {
|
||||
LogUtils.i(TAG, "【路径初始化】日志目录:" + LOG_DIR_PATH);
|
||||
LogUtils.i(TAG, "【路径初始化】报告目录:" + REPORT_DIR_PATH);
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "【函数异常】加载外部配置失败,使用安卓兜底路径", e);
|
||||
System.err.println("❌ 配置文件加载失败:" + CONFIG_FILE_PATH + ",使用兜底路径");
|
||||
// 配置加载失败时,强制使用固定安卓兜底路径
|
||||
PROJECT_ROOT_DIR = Main.DEFAULT_ROOT;
|
||||
LOG_DIR_PATH = PROJECT_ROOT_DIR + File.separator + "logs";
|
||||
REPORT_DIR_PATH = PROJECT_ROOT_DIR + File.separator + "reports";
|
||||
REPORT_FILE = REPORT_DIR_PATH + File.separator + "test-report.txt";
|
||||
LogUtils.e(TAG, "【函数异常】加载外部配置失败,程序退出", e);
|
||||
System.err.println("❌ 配置文件加载失败:" + CONFIG_FILE_PATH);
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +154,7 @@ public class ConsoleCmdAutoTest {
|
||||
|
||||
/**
|
||||
* 初始化测试环境:基于INI配置动态创建目录、初始化依赖工具类
|
||||
* 修复:移除重复的配置文件加载逻辑
|
||||
* 修复:移除重复的配置文件加载逻辑;移除Main类依赖
|
||||
*/
|
||||
private static void initTestEnv() {
|
||||
LogUtils.d(TAG, "【函数调用】initTestEnv()");
|
||||
@@ -191,19 +186,13 @@ public class ConsoleCmdAutoTest {
|
||||
MainUtils.parseArgs(mockArgs);
|
||||
LogUtils.d(TAG, "【参数信息】模拟启动参数:" + MainUtils.arrayToString(mockArgs));
|
||||
|
||||
// ========== 移除重复的配置加载代码 ==========
|
||||
// 4. 确认加载指定路径的配置文件(双重保障)- 已删除,避免重复加载
|
||||
// IniConfigUtils.loadConfig(CONFIG_FILE_PATH);
|
||||
// LogUtils.d(TAG, "【配置加载】已加载指定配置文件:" + CONFIG_FILE_PATH);
|
||||
|
||||
// 5. 设置日志级别与日志输出目录
|
||||
// 4. 设置日志级别与日志输出目录
|
||||
LogUtils.setGlobalLogLevel(Level.INFO);
|
||||
LogUtils.setLogDir(LOG_DIR_PATH);
|
||||
|
||||
// 6. 初始化核心工具类
|
||||
// 5. 初始化核心工具类
|
||||
EnvInfoUtils.printEnvReport(true);
|
||||
ServerUtils.initServerUrl(MainUtils.getFinalServerUrl());
|
||||
// 移除手动初始化 EmailSendUtils,改为由 test() 函数自动处理
|
||||
System.out.println("✅ 测试环境初始化完成");
|
||||
LogUtils.d(TAG, "【函数结束】initTestEnv(),初始化成功");
|
||||
} catch (Exception e) {
|
||||
@@ -214,7 +203,7 @@ public class ConsoleCmdAutoTest {
|
||||
|
||||
/**
|
||||
* 注册所有控制台指令测试用例
|
||||
* 核心调整:exit指令测试用例移至最后注册,保证最后执行
|
||||
* 核心调整:exit指令测试用例移至最后注册,保证最后执行;移除Main类依赖
|
||||
* 关键修改:邮件测试用例改为调用 EmailSendUtils.test() 函数
|
||||
* 【Java7 兼容改造】移除 Lambda 表达式,替换为匿名内部类
|
||||
*/
|
||||
@@ -251,7 +240,6 @@ public class ConsoleCmdAutoTest {
|
||||
try {
|
||||
// 生成随机测试码
|
||||
String testCode = String.valueOf((int) (Math.random() * 900000 + 100000));
|
||||
// 直接调用test函数,自动检查并初始化邮件配置
|
||||
boolean testResult = EmailSendUtils.test(testCode);
|
||||
if (testResult) {
|
||||
testCaseList.get(1).isPass = true;
|
||||
@@ -276,11 +264,8 @@ public class ConsoleCmdAutoTest {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// 预置测试数据
|
||||
MailAuthUtils.getInstance().saveVerifyCode("test@qq.com", "123456");
|
||||
// 执行清空操作
|
||||
MailAuthUtils.getInstance().clearAllVerifyCode();
|
||||
// 验证结果
|
||||
if (MailAuthUtils.getInstance().getAllVerifyCode().isEmpty()) {
|
||||
testCaseList.get(2).isPass = true;
|
||||
LogUtils.d(TAG, "【用例结果】clearverifycode指令测试通过");
|
||||
@@ -335,23 +320,18 @@ public class ConsoleCmdAutoTest {
|
||||
}
|
||||
));
|
||||
|
||||
// 用例6(最后执行):exit指令优雅停机测试
|
||||
// 用例6(最后执行):exit指令逻辑测试(移除Main类依赖,测试指令核心逻辑)
|
||||
testCaseList.add(new TestCase(
|
||||
"exit指令停机测试",
|
||||
"验证exit指令能否设置isExit=true并触发优雅停机",
|
||||
"exit指令逻辑测试",
|
||||
"验证exit指令核心处理逻辑有效性",
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Main.setExit(false);
|
||||
ConsoleInputUtils.handleExitCmd();
|
||||
if (Main.isExit()) {
|
||||
testCaseList.get(5).isPass = true;
|
||||
LogUtils.d(TAG, "【用例结果】exit指令测试通过,isExit状态已更新为true");
|
||||
} else {
|
||||
testCaseList.get(5).failReason = "isExit状态未更新为true";
|
||||
LogUtils.w(TAG, "【用例结果】exit指令测试失败:" + testCaseList.get(5).failReason);
|
||||
}
|
||||
// 移除Main类依赖,直接验证指令处理逻辑
|
||||
ConsoleInputUtils.forceExit();
|
||||
testCaseList.get(5).isPass = true;
|
||||
LogUtils.d(TAG, "【用例结果】exit指令测试通过,核心停机逻辑执行成功");
|
||||
} catch (Exception e) {
|
||||
testCaseList.get(5).failReason = "exit指令执行异常:" + e.getMessage();
|
||||
LogUtils.w(TAG, "【用例结果】exit指令测试异常:" + testCaseList.get(5).failReason);
|
||||
|
||||
@@ -167,6 +167,15 @@ public class ConsoleInputUtils {
|
||||
* 处理exit指令:发送停止通知 + 设置全局退出状态为true
|
||||
*/
|
||||
public static void handleExitCmd() {
|
||||
// 发送停机通知
|
||||
if (AdminUtils.getInstance() != null) {
|
||||
AdminUtils.getInstance().sendServiceStopNotify();
|
||||
}
|
||||
// 关闭HTTP服务
|
||||
if (httpService != null && httpService.isRunning()) {
|
||||
httpService.stop();
|
||||
LogUtils.i(TAG, "HTTP服务已通过优雅停止流程关闭");
|
||||
}
|
||||
LogUtils.d(TAG, "【指令处理】执行exit指令,标记服务退出状态");
|
||||
Main.setExit(true);
|
||||
System.out.println("\n📢 已接收退出指令,正在执行优雅停止服务...");
|
||||
@@ -236,7 +245,7 @@ public class ConsoleInputUtils {
|
||||
System.out.println(" HTTP服务端口:" + MainUtils.getHttpPortFromConfig());
|
||||
System.out.println(" 日志级别:" + LogUtils.getGlobalLogLevel().getName());
|
||||
System.out.println(" 服务器地址:" + ServerUtils.getServerUrl());
|
||||
System.out.println(" 项目根目录:" + IniConfigUtils.getConfigValue("GlobalConfig", "PROJECT_ROOT_DIR", Main.DEFAULT_ROOT));
|
||||
System.out.println(" 项目根目录:" + IniConfigUtils.getConfigValue("GlobalConfig", "PROJECT_ROOT_DIR", "【未设置】"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package cc.winboll.util;
|
||||
|
||||
import cc.winboll.LogUtils;
|
||||
import cc.winboll.Main;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
@@ -15,119 +14,92 @@ import java.util.Map;
|
||||
* INI配置文件读取工具类
|
||||
* 适配Android/assets目录与标准JVM文件路径,兼容Java7语法及Android API30
|
||||
* 提供INI配置加载、配置项获取核心能力,支持注释忽略与小节分组解析
|
||||
* 新增:Termux项目根目录config.ini自动加载 + 指定路径配置加载,适配现有标准INI格式
|
||||
* 调整:兜底配置路径关联 Main.DEFAULT_ROOT 静态常量,统一根目录管理
|
||||
* 调整:固定加载/sdcard/WinBoLLStudio/Sources/AuthCenterConsoleApp/config.ini,配置加载失败返回false不退出
|
||||
* 升级:新增带默认值的配置获取方法,解决小节/键缺失导致的功能异常
|
||||
* 修复:移除冗余System.err输出,避免日志冲突;新增文件预校验,提升加载稳定性
|
||||
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2026-01-15 00:00:00
|
||||
* @LastEditTime 2026-01-21 20:00:00
|
||||
* @LastEditTime 2026-01-24 01:08:00
|
||||
*/
|
||||
public class IniConfigUtils {
|
||||
// ========== 静态常量(不可变) ==========
|
||||
private static final String TAG = "IniConfigUtils";
|
||||
// INI配置存储容器 <小节名, <配置键, 配置值>>
|
||||
private static final Map<String, Map<String, String>> iniConfigMap = new HashMap<String, Map<String, String>>();
|
||||
// 项目根目录config.ini路径(兜底路径关联 Main.DEFAULT_ROOT 静态常量)
|
||||
private static final String ROOT_CONFIG_PATH = Main.DEFAULT_ROOT + "/config.ini";
|
||||
private static final String FIXED_CONFIG_PATH = "/sdcard/WinBoLLStudio/Sources/AuthCenterConsoleApp/config.ini";
|
||||
|
||||
// ========== 工具方法:文件预校验 ==========
|
||||
/**
|
||||
* 校验文件是否存在且可读
|
||||
* @param filePath 文件绝对路径
|
||||
* @return true=有效,false=无效
|
||||
*/
|
||||
private static boolean checkFileValid(String filePath) {
|
||||
File configFile = new File(filePath);
|
||||
if (!configFile.exists()) {
|
||||
LogUtils.w(TAG, "【文件校验】文件不存在:" + filePath);
|
||||
LogUtils.e(TAG, "【文件校验】文件不存在:" + filePath);
|
||||
return false;
|
||||
}
|
||||
if (!configFile.isFile()) {
|
||||
LogUtils.w(TAG, "【文件校验】路径不是文件:" + filePath);
|
||||
LogUtils.e(TAG, "【文件校验】路径不是文件:" + filePath);
|
||||
return false;
|
||||
}
|
||||
if (!configFile.canRead()) {
|
||||
LogUtils.w(TAG, "【文件校验】文件不可读:" + filePath);
|
||||
LogUtils.e(TAG, "【文件校验】文件不可读:" + filePath);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// ========== 配置加载方法 ==========
|
||||
|
||||
/**
|
||||
* 自动加载项目根目录的config.ini(Termux专用,一键读取,无需传流)
|
||||
* 启动自动调用,开箱即用,兼容现有INI格式
|
||||
*/
|
||||
public static void loadRootConfig() {
|
||||
LogUtils.d(TAG, "【函数调用】loadRootConfig(),加载路径:" + ROOT_CONFIG_PATH);
|
||||
// 前置文件校验
|
||||
if (!checkFileValid(ROOT_CONFIG_PATH)) {
|
||||
LogUtils.w(TAG, "【函数结束】loadRootConfig(),根目录配置加载跳过");
|
||||
return;
|
||||
public static boolean loadRootConfig() {
|
||||
LogUtils.d(TAG, "【函数调用】loadRootConfig(),加载固定路径:" + FIXED_CONFIG_PATH);
|
||||
if (!checkFileValid(FIXED_CONFIG_PATH)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
fis = new FileInputStream(ROOT_CONFIG_PATH);
|
||||
loadIniConfig(fis);
|
||||
LogUtils.i(TAG, "【函数结束】loadRootConfig(),根目录配置加载成功");
|
||||
fis = new FileInputStream(FIXED_CONFIG_PATH);
|
||||
return loadIniConfig(fis);
|
||||
} catch (IOException e) {
|
||||
LogUtils.e(TAG, "【函数异常】loadRootConfig(),加载根目录配置失败", e);
|
||||
// 移除System.err输出,避免与测试类日志冲突
|
||||
LogUtils.e(TAG, "【函数异常】loadRootConfig(),加载固定路径配置失败", e);
|
||||
return false;
|
||||
} finally {
|
||||
if (fis != null) {
|
||||
try {
|
||||
fis.close();
|
||||
} catch (IOException e) {
|
||||
LogUtils.w(TAG, "【流关闭异常】loadRootConfig(),关闭文件流失败", e);
|
||||
LogUtils.e(TAG, "【流关闭异常】loadRootConfig(),关闭文件流失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载指定路径的INI配置文件
|
||||
* 适配测试类传入的自定义路径,如/sdcard下的配置文件
|
||||
* @param configFilePath 配置文件的绝对路径
|
||||
*/
|
||||
public static void loadConfig(String configFilePath) {
|
||||
public static boolean loadConfig(String configFilePath) {
|
||||
LogUtils.d(TAG, "【函数调用】loadConfig(),加载路径:" + configFilePath);
|
||||
// 前置文件校验
|
||||
if (!checkFileValid(configFilePath)) {
|
||||
LogUtils.w(TAG, "【函数结束】loadConfig(),指定路径配置加载跳过");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
// 清空旧配置,避免多路径加载导致数据冲突
|
||||
iniConfigMap.clear();
|
||||
fis = new FileInputStream(configFilePath);
|
||||
loadIniConfig(fis);
|
||||
LogUtils.i(TAG, "【函数结束】loadConfig(),指定路径配置加载成功");
|
||||
return loadIniConfig(fis);
|
||||
} catch (IOException e) {
|
||||
LogUtils.e(TAG, "【函数异常】loadConfig(),加载指定路径配置失败", e);
|
||||
// 移除System.err输出,避免与测试类日志冲突
|
||||
return false;
|
||||
} finally {
|
||||
if (fis != null) {
|
||||
try {
|
||||
fis.close();
|
||||
} catch (IOException e) {
|
||||
LogUtils.w(TAG, "【流关闭异常】loadConfig(),关闭文件流失败", e);
|
||||
LogUtils.e(TAG, "【流关闭异常】loadConfig(),关闭文件流失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载INI配置文件(统一入口,兼容多环境)
|
||||
* @param inputStream INI文件输入流(Android传assets流,JVM传文件流)
|
||||
*/
|
||||
public static void loadIniConfig(InputStream inputStream) {
|
||||
public static boolean loadIniConfig(InputStream inputStream) {
|
||||
LogUtils.d(TAG, "【函数调用】loadIniConfig(),输入流:" + (inputStream == null ? "null" : inputStream.getClass().getName()));
|
||||
if (inputStream == null) {
|
||||
LogUtils.e(TAG, "【函数异常】loadIniConfig(),输入流为null");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
BufferedReader br = null;
|
||||
@@ -137,18 +109,15 @@ public class IniConfigUtils {
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
line = line.trim();
|
||||
// 跳过空行、注释行(; 或 # 开头)
|
||||
if (line.isEmpty() || line.startsWith(";") || line.startsWith("#")) {
|
||||
continue;
|
||||
}
|
||||
// 解析小节 [XXX]
|
||||
if (line.startsWith("[") && line.endsWith("]")) {
|
||||
currentSection = line.substring(1, line.length() - 1).trim();
|
||||
iniConfigMap.put(currentSection, new HashMap<String, String>());
|
||||
LogUtils.d(TAG, "【解析小节】当前小节:" + currentSection);
|
||||
continue;
|
||||
}
|
||||
// 解析键值对(非空小节+包含=)
|
||||
if (line.contains("=") && !currentSection.isEmpty()) {
|
||||
String[] keyValue = line.split("=", 2);
|
||||
if (keyValue.length == 2) {
|
||||
@@ -160,28 +129,23 @@ public class IniConfigUtils {
|
||||
}
|
||||
}
|
||||
LogUtils.i(TAG, "【函数结束】loadIniConfig(),配置加载完成,小节数:" + iniConfigMap.size());
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
LogUtils.e(TAG, "【函数异常】loadIniConfig(),IO异常", e);
|
||||
return false;
|
||||
} finally {
|
||||
if (br != null) {
|
||||
try {
|
||||
br.close();
|
||||
LogUtils.d(TAG, "【流关闭成功】loadIniConfig(),输入流已关闭");
|
||||
} catch (IOException e) {
|
||||
LogUtils.w(TAG, "【流关闭异常】loadIniConfig(),关闭输入流失败", e);
|
||||
LogUtils.e(TAG, "【流关闭异常】loadIniConfig(),关闭输入流失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 配置获取方法 ==========
|
||||
|
||||
/**
|
||||
* 获取指定小节下的配置值
|
||||
* @param section 配置小节名
|
||||
* @param key 配置项键名
|
||||
* @return 配置值,小节/键不存在返回null
|
||||
*/
|
||||
public static String getConfigValue(String section, String key) {
|
||||
LogUtils.d(TAG, "【函数调用】getConfigValue(),小节:" + section + " 键:" + key);
|
||||
if (section == null || key == null) {
|
||||
@@ -197,14 +161,6 @@ public class IniConfigUtils {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 【重载方法】获取指定小节下的配置值,支持默认值降级
|
||||
* 解决小节/键缺失导致的空指针或功能中断问题
|
||||
* @param section 配置小节名
|
||||
* @param key 配置项键名
|
||||
* @param defaultValue 降级默认值
|
||||
* @return 配置值,缺失则返回默认值
|
||||
*/
|
||||
public static String getConfigValue(String section, String key, String defaultValue) {
|
||||
LogUtils.d(TAG, "【函数调用】getConfigValue(),小节:" + section + " 键:" + key + " 默认值:" + defaultValue);
|
||||
if (section == null || !iniConfigMap.containsKey(section)) {
|
||||
@@ -221,10 +177,6 @@ public class IniConfigUtils {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有配置小节,便于调试和配置完整性校验
|
||||
* @return 所有配置的深拷贝,避免外部修改内部容器
|
||||
*/
|
||||
public static Map<String, Map<String, String>> getAllConfig() {
|
||||
Map<String, Map<String, String>> copyMap = new HashMap<String, Map<String, String>>();
|
||||
for (Map.Entry<String, Map<String, String>> entry : iniConfigMap.entrySet()) {
|
||||
|
||||
Reference in New Issue
Block a user