适配AIDE Pro编译器运行。添加单元测试邮件发送功能。添加系统信息发送。

This commit is contained in:
2026-01-20 16:56:00 +08:00
parent ca71e3469d
commit 448d96d5aa
23 changed files with 1021 additions and 335 deletions

View File

@@ -3,6 +3,7 @@
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/jdk1.7.0_45"/>
<classpathentry kind="output" path="bin"/>
<classpathentry kind="lib" path="libs/nanohttpd-2.3.1.jar"/>
<classpathentry kind="lib" path="libs/jakarta.mail-1.6.7.jar"/>
<classpathentry kind="lib" path="libs/activation-1.1.1.jar"/>
<classpathentry kind="lib" path="libs/android-mail-1.6.2.jar"/>
<classpathentry kind="lib" path="libs/android-30.jar"/>
</classpath>

View File

@@ -4,6 +4,8 @@ bash bash/start_winboll_in_termux.sh
测试命令
http://localhost:8080/authcenter/ping
服务介绍
http://localhost:8080/authcenter/login?action=
# 服务器端配置
服务器编译命令
@@ -37,3 +39,13 @@ AdminUtils.getInstance().sendTemporaryNotify(
"系统于2026-01-22 15:00 更新了邮件服务器地址,请留意后续通知是否正常。"
);
邮件类库使用
// Source: https://mvnrepository.com/artifact/javax.activation/activation
implementation 'javax.activation:activation:1.1.1'
// Source: https://mvnrepository.com/artifact/com.sun.mail/javax.mail
implementation 'com.sun.mail:javax.mail:1.4.7'
// Source: https://mvnrepository.com/artifact/javax.mail/javax.mail-api
implementation 'javax.mail:javax.mail-api:1.4.7'
// Source: https://mvnrepository.com/artifact/com.sun.mail/android-mail
runtimeOnly 'com.sun.mail:android-mail:1.6.2'

BIN
libs/android-mail-1.6.2.jar Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,5 +1,5 @@
import cc.winboll.LogUtils;
import cc.winboll.auth.RSAUtils;
import cc.winboll.test.ConsoleCmdAutoTest;
import cc.winboll.util.IniConfigUtils;
import java.util.Date;
import java.util.logging.Level;
@@ -45,9 +45,7 @@ public class Main {
System.out.println("\n【配置日志测试】...");
// 单元测试
LogUtils.test(); // 执行测试
RSAUtils.test();
//ConsoleCmdAutoTest.main(args);
ConsoleCmdAutoTest.main(args);
LogUtils.i("Main", "正在处理事务...");

View File

@@ -24,7 +24,7 @@ import java.util.logging.Level;
* 支持控制台输入交互输入help查帮助、输入exit退出、其他指令提示未识别
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Date 2026-01-14 00:00:00
* @LastEditTime 2026-01-26 17:18:36
* @LastEditTime 2026-01-26 适配邮件工具单例调用
*/
public class WinBoLL {
// ========== 静态属性(常量在前 变量在后,顺序规整) ==========
@@ -61,7 +61,8 @@ public class WinBoLL {
// 环境信息打印 + 工具类初始化
EnvInfoUtils.printEnvReport(showDetailEnv);
initServerUtils(serverUrl);
EmailSendUtils.initEmailConfig();
// 调整:邮件工具单例初始化,增加结果判断+日志输出
initEmailSendUtils();
// 启动配置校验
boolean initCheckPass = InitCheckUtils.checkAllInitConfig();
@@ -103,6 +104,22 @@ public class WinBoLL {
LogUtils.d(TAG, "【函数结束】initServerUtils(),服务器工具类初始化就绪");
}
/**
* 调整:独立封装邮件工具初始化,适配单例,增加状态日志
*/
private static void initEmailSendUtils() {
LogUtils.d(TAG, "【函数调用】initEmailSendUtils(),开始初始化邮件发送工具");
EmailSendUtils emailUtils = EmailSendUtils.getInstance();
boolean initResult = emailUtils.initEmailConfig();
if (initResult) {
System.out.println("✅ EmailSendUtils 初始化完成,可正常发送邮件");
LogUtils.i(TAG, "邮件发送工具初始化就绪");
} else {
System.out.println("⚠️ EmailSendUtils 初始化失败邮件相关功能不可用请检查INI邮箱配置");
LogUtils.w(TAG, "邮件发送工具初始化失败,不影响核心服务启动");
}
}
/**
* 启动AuthCenter HTTP服务绑定控制台重启实例
*/

View File

@@ -0,0 +1,5 @@
package cc.winboll.auth;
public class ActionUtils
{
}

View File

@@ -14,7 +14,7 @@ import java.util.concurrent.TimeUnit;
* 适配Java7语法兼容项目邮件工具类与Termux环境支持验证码时效自动管控
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Date 2026/01/17 10:00:00
* @LastEditTime 2026/01/20 20:00:00
* @LastEditTime 2026/01/26 适配邮件工具单例调用
*/
public class MailAuthUtils {
private static final String TAG = "MailAuthUtils";
@@ -71,8 +71,21 @@ public class MailAuthUtils {
String verifyCode = generateVerifyCode();
// 2. 计算过期时间戳(当前时间+有效期,单位毫秒)
long expireTime = System.currentTimeMillis() + CODE_EXPIRE_MINUTES * 60 * 1000L;
// 3. 发送验证码邮件
boolean sendResult = EmailSendUtils.sendVerifyCodeEmail(trimEmail, verifyCode);
// ========== 核心调整:适配邮件单例 + 补充初始化判断 ==========
EmailSendUtils emailUtils = EmailSendUtils.getInstance();
// 未初始化则执行初始化,失败直接返回
if (!emailUtils.isConfigInited()) {
LogUtils.d(TAG, "邮件工具未就绪,执行初始化流程");
boolean initOk = emailUtils.initEmailConfig();
if (!initOk) {
LogUtils.e(TAG, "邮件工具初始化失败,无法发送验证码");
return null;
}
}
// 3. 单例调用发送验证码邮件
boolean sendResult = emailUtils.sendVerifyCodeEmail(trimEmail, verifyCode);
if (sendResult) {
// 存储格式验证码_过期时间戳
emailCodeExpireMap.put(trimEmail, verifyCode + "_" + expireTime);
@@ -173,7 +186,6 @@ public class MailAuthUtils {
/**
* 辅助方法:生成指定长度纯数字验证码
* @return 6位数字验证码
*/
private String generateVerifyCode() {
StringBuilder sb = new StringBuilder();

View File

@@ -0,0 +1,5 @@
package cc.winboll.models;
public class ActionModel
{
}

View File

@@ -0,0 +1,194 @@
package cc.winboll.models;
import cc.winboll.LogUtils;
import java.util.Date;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* 用户模型类密码MD5加密存储+密码校验+LogUtils日志输出
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Date 2026-01-20 11:45:00
* @LastEditTime 2026-01-20 12:05:00
*/
public class UserModel {
// 成员变量(按业务逻辑排序)
private Long userId; // 用户ID
private String userEmail; // 用户邮箱
private String userPassword; // 用户密码(MD5加密后存储)
private String token; // 连接token
private Date loginTime; // 登录时间
private Date lastTokenUpdateTime; // 最后Token更新时间
private Integer loginCount; // 登录次数计数(默认0)
// 空参构造Java7规范必备
public UserModel() {
this.loginCount = 0; // 登录次数默认初始化0
}
// Getter & Setter 方法(按字段顺序)
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getUserEmail() {
return userEmail;
}
public void setUserEmail(String userEmail) {
this.userEmail = userEmail;
}
// 密码Setter传入明文自动MD5加密后存储
public void setUserPassword(String userPassword) {
this.userPassword = encryptMD5(userPassword);
}
// 密码Getter返回加密后密码不对外暴露明文
public String getUserPassword() {
return userPassword;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public Date getLoginTime() {
return loginTime;
}
public void setLoginTime(Date loginTime) {
this.loginTime = loginTime;
}
public Date getLastTokenUpdateTime() {
return lastTokenUpdateTime;
}
public void setLastTokenUpdateTime(Date lastTokenUpdateTime) {
this.lastTokenUpdateTime = lastTokenUpdateTime;
}
public Integer getLoginCount() {
return loginCount;
}
public void setLoginCount(Integer loginCount) {
this.loginCount = loginCount;
}
// 登录次数自增方法
public void incrementLoginCount() {
this.loginCount += 1;
}
/**
* 校验密码是否正确
* @param checkPwd 待校验的明文密码
* @return 密码匹配返回true不匹配返回false
*/
public boolean checkPassword(String checkPwd) {
// 空值防护,避免空指针
if (this.userPassword == null || checkPwd == null) {
LogUtils.w("UserModel", "密码校验:待校验密码或存储密码为空");
return false;
}
// 明文加密后和存储的密文对比
boolean match = this.userPassword.equals(encryptMD5(checkPwd));
LogUtils.d("UserModel", "密码校验结果:" + (match ? "匹配成功" : "匹配失败"));
return match;
}
/**
* Java7兼容 MD5加密工具方法(32位小写)
* @param plainText 明文
* @return 加密后32位小写字符串异常返回null
*/
private String encryptMD5(String plainText) {
if (plainText == null) {
LogUtils.w("UserModel", "MD5加密明文密码为null");
return null;
}
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] bytes = md.digest(plainText.getBytes());
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
int val = b & 0xFF;
if (val < 16) {
sb.append("0");
}
sb.append(Integer.toHexString(val));
}
return sb.toString();
} catch (NoSuchAlgorithmException e) {
LogUtils.e("UserModel", "MD5加密算法获取失败", e);
return null;
}
}
// 重写toString隐藏密码方便调试
@Override
public String toString() {
return "UserModel{" +
"userId=" + userId +
", userEmail='" + userEmail + '\'' +
", token='" + token + '\'' +
", loginTime=" + loginTime +
", lastTokenUpdateTime=" + lastTokenUpdateTime +
", loginCount=" + loginCount +
'}';
}
/**
* 静态测试方法用LogUtils输出日志验证核心功能
*/
public static void test() {
String tag = "UserModelTest";
LogUtils.i(tag, "==================================== UserModel 测试开始 ====================================");
// 1. 实例化+基础字段赋值
UserModel user = new UserModel();
user.setUserId(1001L);
user.setUserEmail("test@winboll.cc");
user.setToken("TOKEN_2026_ABC123");
user.setLoginTime(new Date());
user.setLastTokenUpdateTime(new Date());
LogUtils.i(tag, "1. 用户基础字段赋值完成");
// 2. 密码加密&校验测试
String plainPwd = "123456";
user.setUserPassword(plainPwd);
LogUtils.d(tag, "2. 明文密码:" + plainPwd);
LogUtils.d(tag, "3. MD5加密后密码" + user.getUserPassword());
boolean pwdRight = user.checkPassword(plainPwd);
boolean pwdWrong = user.checkPassword("654321");
LogUtils.i(tag, "4. 正确密码校验结果:" + pwdRight + "预期true");
LogUtils.i(tag, "5. 错误密码校验结果:" + pwdWrong + "预期false");
// 3. 登录次数测试
LogUtils.i(tag, "6. 初始登录次数:" + user.getLoginCount() + "预期0");
user.incrementLoginCount();
user.incrementLoginCount();
LogUtils.i(tag, "7. 2次自增后登录次数" + user.getLoginCount() + "预期2");
// 4. 空密码校验测试
UserModel emptyPwdUser = new UserModel();
boolean emptyPwdCheck = emptyPwdUser.checkPassword(null);
LogUtils.i(tag, "8. 空密码校验结果:" + emptyPwdCheck + "预期false");
// 5. 完整对象打印
LogUtils.i(tag, "9. 用户对象完整信息:" + user.toString());
LogUtils.i(tag, "==================================== UserModel 测试结束 ====================================\n");
}
}

View File

@@ -2,47 +2,46 @@ package cc.winboll.test;
import cc.winboll.LogUtils;
import cc.winboll.auth.MailAuthUtils;
import cc.winboll.auth.RSAUtils;
import cc.winboll.models.UserModel;
import cc.winboll.util.ConsoleInputUtils;
import cc.winboll.util.EmailSendUtils;
import cc.winboll.util.EnvInfoUtils;
import cc.winboll.util.HelpInfoUtils;
import cc.winboll.util.IniConfigUtils;
import cc.winboll.util.MainUtils;
import cc.winboll.util.ServerUtils;
import cc.winboll.util.IniConfigUtils;
import cc.winboll.util.EnvInfoUtils;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
/**
* AuthCenter 控制台指令自动化测试类
* 适配 Java7 语言规范与 Android API30 运行环境
* 自动执行控制台指令测试用例,生成结构化测试报告
* 自动执行控制台指令测试用例,生成结构化测试报告 + 发送报告到配置邮箱
* 核心优化从config.ini读取配置动态初始化测试路径与参数根目录配置缺失直接日志退出
* 调整:邮件测试用例改为调用 EmailSendUtils.test() 函数,利用自动初始化特性
* 新增loadExternalConfig配置加载成功后输出提示信息
* 修复移除重复配置加载消除矛盾日志输出移除Main.DEFAULT_ROOT依赖
* 新增LogUtils专项测试用例验证日志工具基础功能有效性
* 本次更新1.新增EnvInfoUtils测试用例 2.邮件报告追加环境信息 3.用EmailSendUtils无参test()+修复用例索引错误+精简冗余日志
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Date 2026/01/20 10:00:00
* @LastEditTime 2026/01/24 补充LogUtils测试用例
* @LastEditTime 2026/01/26 新增EnvInfoUtils用例+邮件追加环境信息+补充用户ID展示
*/
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";
private static final String CONFIG_KEY_TEST_REPORT_DIR = "test_report_dir";
private static final String CONFIG_KEY_REPORTS_PATH = "reports_path";// 报告目录配置键
private static final String CONFIG_KEY_LOG_PATH = "log_path";// 日志目录配置键
private static final String CONFIG_SECTION_GLOBAL = "GlobalConfig";
private static final String CONFIG_SECTION_TEST = "TestConfig";
private static final String CONFIG_SECTION_EMAIL = "EmailConfig";
private static final String CONFIG_KEY_SEND_EMAIL = "send_email_account";
// ========== 动态配置属性从INI读取可变 ==========
private static String PROJECT_ROOT_DIR;
private static String CONFIG_FILE_PATH;
private static String LOG_DIR_PATH;
private static String REPORT_DIR_PATH;
private static String REPORT_FILE;
@@ -73,15 +72,17 @@ public class ConsoleCmdAutoTest {
public static void main(String[] args) {
LogUtils.d(TAG, "【函数调用】main(),自动化测试程序启动");
// 1. 加载外部config.ini配置缺失直接退出
loadExternalConfig(args);
// 初始化报告路径读取GlobalConfig的reports_path优先级最高
initReportPathFromIni();
// 2. 执行测试流程
initTestEnv();
// 直接执行测试流程(依赖外部已初始化配置)
registerTestCases();
runAllTestCases();
generateTestReport();
// 新增:生成报告后发送到配置邮箱
sendReportToEmail();
// 输出测试总结
String summary = String.format("测试完成!总计:%d 用例 | 通过:%d | 失败:%d",
testCaseList.size(), passCount, failCount);
@@ -91,169 +92,167 @@ public class ConsoleCmdAutoTest {
LogUtils.i(TAG, "【函数结束】main()" + summary);
}
// ========== 新增核心方法:从启动参数/默认路径加载外部config.ini ==========
/**
* 从启动参数读取config.ini路径无参数则使用当前目录下的config.ini
* 启动参数格式:-config:xxx/config.ini
* 核心调整PROJECT_ROOT_DIR缺失直接日志退出移除兜底路径
*/
private static void loadExternalConfig(String[] args) {
LogUtils.d(TAG, "【函数调用】loadExternalConfig(),加载外部配置文件");
// 默认配置文件路径当前目录下的config.ini
String defaultConfigPath = new File("config.ini").getAbsolutePath();
CONFIG_FILE_PATH = defaultConfigPath;
// 解析启动参数中的配置文件路径
if (args != null && args.length > 0) {
for (String arg : args) {
String trimArg = arg.trim();
if (trimArg.startsWith("-config:")) {
CONFIG_FILE_PATH = trimArg.substring(8).trim();
LogUtils.i(TAG, "【配置路径】从启动参数读取配置文件:" + CONFIG_FILE_PATH);
break;
}
}
}
// 加载配置文件,读取核心路径参数
// ========== 核心修改读取GlobalConfig的reports_path初始化报告路径 ==========
private static void initReportPathFromIni() {
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, "");
LOG_DIR_PATH = IniConfigUtils.getConfigValue(CONFIG_SECTION_GLOBAL, CONFIG_KEY_LOG_PATH, "logs");
// 核心修改读取GlobalConfig下的reports_path作为报告根目录
REPORT_DIR_PATH = IniConfigUtils.getConfigValue(CONFIG_SECTION_GLOBAL, CONFIG_KEY_REPORTS_PATH, "test/reports");
// 读取项目根目录(必填),缺失直接日志退出
PROJECT_ROOT_DIR = IniConfigUtils.getConfigValue(CONFIG_SECTION_GLOBAL, CONFIG_KEY_ROOT_DIR);
if (PROJECT_ROOT_DIR == null || PROJECT_ROOT_DIR.trim().isEmpty()) {
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();
// 拼接完整报告文件路径,文件名带时间戳防重复
REPORT_FILE = new File(REPORT_DIR_PATH, "AuthCenter_TestReport_" + MainUtils.getCurrentTime().replaceAll("[: ]", "_") + ".txt").getAbsolutePath();
// 读取测试报告目录可选兜底为根目录下的reports
String reportDir = IniConfigUtils.getConfigValue(CONFIG_SECTION_TEST, CONFIG_KEY_TEST_REPORT_DIR);
REPORT_DIR_PATH = (reportDir == null || reportDir.trim().isEmpty())
? PROJECT_ROOT_DIR + File.separator + "reports"
: PROJECT_ROOT_DIR + File.separator + reportDir.trim();
// 日志目录根目录下的logs
LOG_DIR_PATH = PROJECT_ROOT_DIR + File.separator + "logs";
// 报告文件路径
REPORT_FILE = REPORT_DIR_PATH + File.separator + "test-report.txt";
LogUtils.i(TAG, "【路径初始化】项目根目录:" + PROJECT_ROOT_DIR);
LogUtils.i(TAG, "【路径初始化】日志目录:" + LOG_DIR_PATH);
LogUtils.i(TAG, "【路径初始化】报告目录:" + REPORT_DIR_PATH);
// 自动创建目录(多级目录也能创建
new File(REPORT_DIR_PATH).mkdirs();
LogUtils.d(TAG, "报告路径初始化完成:" + REPORT_FILE);
LogUtils.d(TAG, "日志目录路径:" + LOG_DIR_PATH);
} catch (Exception e) {
LogUtils.e(TAG, "【函数异常】加载外部配置失败,程序退出", e);
System.err.println("❌ 配置文件加载失败:" + CONFIG_FILE_PATH);
System.exit(1);
LogUtils.e(TAG, "报告路径初始化失败,使用默认路径兜底", e);
// 兜底路径
REPORT_DIR_PATH = "./test/reports";
REPORT_FILE = "./test/reports/AuthCenter_TestReport_" + MainUtils.getCurrentTime().replaceAll("[: ]", "_") + ".txt";
new File(REPORT_DIR_PATH).mkdirs();
}
}
// ========== 新增:发送测试报告到配置邮箱(追加环境信息) ==========
private static void sendReportToEmail() {
LogUtils.d(TAG, "【函数调用】sendReportToEmail(),开始发送测试报告邮件");
EmailSendUtils emailUtils = EmailSendUtils.getInstance();
// 1. 获取配置的发送邮箱(收件人=发送人)
String receiveEmail = IniConfigUtils.getConfigValue(CONFIG_SECTION_EMAIL, CONFIG_KEY_SEND_EMAIL, "");
if (EmailSendUtils.isStrEmpty(receiveEmail)) {
LogUtils.w(TAG, "未获取到配置的邮箱账号,跳过邮件发送");
return;
}
// 2. 读取报告文件内容
String reportContent = readReportFile();
if (reportContent == null) {
LogUtils.e(TAG, "读取测试报告失败,无法发送邮件");
return;
}
// 核心更新拼接EnvInfoUtils完整环境报告格式和终端一致
String envInfo = getEnvReportForEmail();
String finalContent = reportContent + "\n\n" + envInfo;
// 3. 发送邮件 适配单例
String emailSubject = "【AuthCenter 自动化测试报告】" + MainUtils.getCurrentTime();
try {
// 初始化邮件配置(确保配置生效)
if (!emailUtils.isConfigInited()) {
emailUtils.initEmailConfig();
}
// 发送拼接环境信息后的完整内容
boolean sendSuccess = emailUtils.sendTextEmail(receiveEmail, emailSubject, finalContent);
if (sendSuccess) {
LogUtils.i(TAG, "测试报告已成功发送到邮箱:" + receiveEmail);
System.out.println("\n✅ 测试报告邮件发送成功!收件邮箱:" + receiveEmail);
} else {
LogUtils.w(TAG, "测试报告邮件发送失败");
System.out.println("\n⚠ 测试报告邮件发送失败,请检查邮箱配置");
}
} catch (Exception e) {
LogUtils.e(TAG, "发送测试报告邮件异常", e);
System.out.println("\n⚠ 测试报告邮件发送异常:" + e.getMessage());
}
}
// ========== 更新:组装邮件用的环境信息(格式统一+无冗余) ==========
private static String getEnvReportForEmail() {
StringBuilder envSb = new StringBuilder();
envSb.append("=== 运行环境核心信息EnvInfoUtils获取===\n");
envSb.append("核心环境标识:").append(EnvInfoUtils.getEnvFlag()).append("\n");
envSb.append("是否Android环境").append(EnvInfoUtils.isAndroidEnv() ? "" : "").append("\n\n");
// 核心:直接拼接完整环境报告,和终端输出完全一致
envSb.append(EnvInfoUtils.getFullEnvReportText()).append("\n");
envSb.append("=====================================");
return envSb.toString();
}
// ========== 新增:读取报告文件内容 ==========
private static String readReportFile() {
BufferedReader reader = null;
StringBuilder content = new StringBuilder();
try {
reader = new BufferedReader(new FileReader(REPORT_FILE));
String line;
while ((line = reader.readLine()) != null) {
content.append(line).append("\n");
}
return content.toString();
} catch (Exception e) {
LogUtils.e(TAG, "读取报告文件异常", e);
return null;
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
LogUtils.e(TAG, "关闭报告文件流异常", e);
}
}
}
}
// ========== 工具函数(按执行顺序排列) ==========
/**
* 初始化测试环境基于INI配置动态创建目录、初始化依赖工具类
* 修复移除重复的配置文件加载逻辑移除Main类依赖
*/
private static void initTestEnv() {
LogUtils.d(TAG, "【函数调用】initTestEnv()");
try {
// 1. 创建日志目录(确保日志正常输出)
File logDir = new File(LOG_DIR_PATH);
if (!logDir.exists()) {
boolean mkdirSuccess = logDir.mkdirs();
if (mkdirSuccess) {
LogUtils.d(TAG, "【路径初始化】日志目录创建成功:" + LOG_DIR_PATH);
} else {
LogUtils.w(TAG, "【路径初始化】日志目录创建失败:" + LOG_DIR_PATH);
}
}
// 2. 创建报告目录(确保测试报告能正常生成)
File reportDir = new File(REPORT_DIR_PATH);
if (!reportDir.exists()) {
boolean mkdirSuccess = reportDir.mkdirs();
if (mkdirSuccess) {
LogUtils.d(TAG, "【路径初始化】报告目录创建成功:" + REPORT_DIR_PATH);
} else {
LogUtils.w(TAG, "【路径初始化】报告目录创建失败:" + REPORT_DIR_PATH);
}
}
// 3. 模拟启动参数并解析(添加配置文件路径参数)
String[] mockArgs = {"-v", "-detail", "-log:INFO", "-config:" + CONFIG_FILE_PATH};
MainUtils.parseArgs(mockArgs);
LogUtils.d(TAG, "【参数信息】模拟启动参数:" + MainUtils.arrayToString(mockArgs));
// 5. 初始化核心工具类
EnvInfoUtils.printEnvReport(true);
ServerUtils.initServerUrl(MainUtils.getFinalServerUrl());
System.out.println("✅ 测试环境初始化完成");
LogUtils.d(TAG, "【函数结束】initTestEnv(),初始化成功");
} catch (Exception e) {
LogUtils.e(TAG, "【函数异常】initTestEnv(),初始化失败", e);
throw new RuntimeException("Test environment initialization failed", e);
}
}
/**
* 注册所有控制台指令测试用例
* 核心调整exit指令测试用例移至最后注册保证最后执行移除Main类依赖
* 关键修改:邮件测试用例改为调用 EmailSendUtils.test() 函数
* 新增LogUtils专项测试用例放在最前面执行
* 【Java7 兼容改造】移除 Lambda 表达式,替换为匿名内部类
*/
private static void registerTestCases() {
LogUtils.d(TAG, "【函数调用】registerTestCases(),开始注册测试用例");
final EmailSendUtils emailUtils = EmailSendUtils.getInstance();
// ========== 新增LogUtils专项测试用例最前面执行) ==========
// 用例01LogUtils基础分级日志输出测试
// ========== 3个核心工具类独立测试优先执行) ==========
testCaseList.add(new TestCase(
"LogUtils基础日志输出测试",
"验证LogUtils debug/info/warn/error分级日志输出有效",
"核心工具类集成测试",
"执行LogUtils/RSAUtils/UserModel独立测试函数验证工具类完整",
new Runnable() {
@Override
public void run() {
try {
LogUtils.d(TAG, "LogUtils DEBUG级别日志测试");
LogUtils.i(TAG, "LogUtils INFO级别日志测试");
LogUtils.w(TAG, "LogUtils WARN级别日志测试");
LogUtils.e(TAG, "LogUtils ERROR级别日志测试");
LogUtils.test(); // 执行LogUtils测试
RSAUtils.test(); // 执行RSAUtils测试
UserModel.test(); // 执行UserModel测试
testCaseList.get(0).isPass = true;
LogUtils.d(TAG, "【用例结果】LogUtils基础日志输出测试通过");
LogUtils.d(TAG, "【用例结果】核心工具类集成测试通过");
} catch (Exception e) {
testCaseList.get(0).failReason = "基础日志输出异常:" + e.getMessage();
LogUtils.w(TAG, "【用例结果】LogUtils基础日志测试失败:" + testCaseList.get(0).failReason);
testCaseList.get(0).failReason = "工具类测试异常:" + e.getMessage();
LogUtils.w(TAG, "【用例结果】核心工具类集成测试失败:" + testCaseList.get(0).failReason);
}
}
}
));
// 用例02LogUtils异常堆栈打印测试
// 新增用例02EnvInfoUtils环境检测工具测试紧跟核心工具类
testCaseList.add(new TestCase(
"LogUtils异常日志打印测试",
"验证LogUtils带异常堆栈的日志输出功能",
"EnvInfoUtils环境检测测试",
"验证环境信息工具类核心功能,输出环境标识+完整环境报告",
new Runnable() {
@Override
public void run() {
try {
// 构造测试异常
Exception testException = new RuntimeException("LogUtils异常打印测试专用异常");
LogUtils.e(TAG, "LogUtils带异常堆栈的日志测试", testException);
// 执行工具类自测
EnvInfoUtils.test();
// 验证核心函数有效性
String envFlag = EnvInfoUtils.getEnvFlag();
String javaVer = EnvInfoUtils.getEnvInfo(EnvInfoUtils.PROP_JAVA_VERSION);
if (!EmailSendUtils.isStrEmpty(envFlag) && !EmailSendUtils.isStrEmpty(javaVer)) {
testCaseList.get(1).isPass = true;
LogUtils.d(TAG, "【用例结果】LogUtils异常日志打印测试通过");
LogUtils.d(TAG, "【用例结果】EnvInfoUtils环境检测测试通过环境标识" + envFlag);
} else {
testCaseList.get(1).failReason = "环境信息获取为空,工具类异常";
LogUtils.w(TAG, "【用例结果】EnvInfoUtils环境检测测试失败" + testCaseList.get(1).failReason);
}
} catch (Exception e) {
testCaseList.get(1).failReason = "异常日志打印异常:" + e.getMessage();
LogUtils.w(TAG, "【用例结果】LogUtils异常日志测试失败" + testCaseList.get(1).failReason);
testCaseList.get(1).failReason = "EnvInfoUtils执行异常:" + e.getMessage();
LogUtils.w(TAG, "【用例结果】EnvInfoUtils环境检测测试异常" + testCaseList.get(1).failReason, e);
}
}
}
));
// 原有用例顺延序号从2开始
// 用例03help指令响应测试
// 用例03help指令响应测试索引同步后移
testCaseList.add(new TestCase(
"help指令响应测试",
"验证help指令能否正常打印帮助信息",
@@ -273,33 +272,45 @@ public class ConsoleCmdAutoTest {
}
));
// 用例04selftestmail指令测试 - 核心修改:调用 EmailSendUtils.test() 函数
// 用例04selftestmail指令测试 - 用无参test()+精简日志+精准排错(索引同步后移)
testCaseList.add(new TestCase(
"selftestmail指令测试",
"验证邮件自测指令能否正常执行需正确配置INI",
"验证邮件自测功能需配置INI邮箱账号、授权码且依赖齐全",
new Runnable() {
@Override
public void run() {
try {
// 生成随机测试码
String testCode = String.valueOf((int) (Math.random() * 900000 + 100000));
boolean testResult = EmailSendUtils.test(testCode);
LogUtils.d(TAG, "邮件自测开始使用无参test自动生成测试码");
// 1. 先检查配置是否就绪,不就绪先初始化
if (!emailUtils.isConfigInited()) {
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);
return;
}
}
// 2. 核心更新调用无参test(),自动生成测试码,简洁高效
boolean testResult = emailUtils.test();
if (testResult) {
testCaseList.get(3).isPass = true;
LogUtils.d(TAG, "【用例结果】selftestmail指令测试通过,测试码:" + testCode);
LogUtils.d(TAG, "【用例结果】selftestmail指令测试通过");
} else {
testCaseList.get(3).failReason = "邮件自测失败,请检查INI配置或邮箱授权码";
testCaseList.get(3).failReason = "邮件发送失败,排查方向1.授权码正确 2.邮箱开启SMTP 3.网络通畅 4.依赖齐全";
LogUtils.w(TAG, "【用例结果】selftestmail指令测试失败" + testCaseList.get(3).failReason);
}
} catch (Exception e) {
testCaseList.get(3).failReason = "邮件自测异常:" + e.getMessage();
LogUtils.w(TAG, "【用例结果】selftestmail指令测试异常" + testCaseList.get(3).failReason);
testCaseList.get(3).failReason = "邮件自测异常:" + e.getMessage() + "已修复AWT依赖优先检查邮件配置";
LogUtils.w(TAG, "【用例结果】selftestmail指令测试异常" + testCaseList.get(3).failReason, e);
}
}
}
));
// 用例05clearverifycode指令测试
// 用例05clearverifycode指令测试(索引同步后移)
testCaseList.add(new TestCase(
"clearverifycode指令测试",
"验证清空验证码指令能否清空MailAuthUtils缓存",
@@ -324,7 +335,7 @@ public class ConsoleCmdAutoTest {
}
));
// 用例06testserver指令测试
// 用例06testserver指令测试(索引同步后移)
testCaseList.add(new TestCase(
"testserver指令测试",
"验证服务器连通性测试指令能否正常执行",
@@ -344,7 +355,7 @@ public class ConsoleCmdAutoTest {
}
));
// 用例07未识别指令测试
// 用例07未识别指令测试(索引同步后移)
testCaseList.add(new TestCase(
"未识别指令测试",
"验证输入未知指令能否正常提示",
@@ -363,7 +374,7 @@ public class ConsoleCmdAutoTest {
}
));
// 用例08最后执行exit指令逻辑测试除Main类依赖测试指令核心逻辑
// 用例08最后执行exit指令逻辑测试索引同步后移)
testCaseList.add(new TestCase(
"exit指令逻辑测试",
"验证exit指令核心处理逻辑有效性",
@@ -371,7 +382,6 @@ public class ConsoleCmdAutoTest {
@Override
public void run() {
try {
// 移除Main类依赖直接验证指令处理逻辑
ConsoleInputUtils.forceExit();
testCaseList.get(7).isPass = true;
LogUtils.d(TAG, "【用例结果】exit指令测试通过核心停机逻辑执行成功");
@@ -419,7 +429,6 @@ public class ConsoleCmdAutoTest {
/**
* 生成结构化测试报告文件
* 调整报告文件路径完全从INI配置动态生成
*/
private static void generateTestReport() {
LogUtils.d(TAG, "【函数调用】generateTestReport(),开始生成测试报告");
@@ -431,8 +440,8 @@ public class ConsoleCmdAutoTest {
writer.write("测试时间:" + MainUtils.getCurrentTime() + "\n");
writer.write("测试环境Java7 + Android API30\n");
writer.write("项目根目录:" + PROJECT_ROOT_DIR + "\n");
writer.write("配置文件路径:" + CONFIG_FILE_PATH + "\n");
writer.write("日志文件夹路径:" + LOG_DIR_PATH + "\n");
writer.write("测试报告路径:" + REPORT_FILE + "\n");
writer.write("=====================================\n");
writer.write("总计用例数:" + testCaseList.size() + "\n");
writer.write("通过用例数:" + passCount + "\n");

View File

@@ -6,19 +6,15 @@ import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.Transport;
/**
* 管理员工具类(单例模式)
* 功能:发送服务启动/关闭/临时通知邮件,携带完整运行环境信息
* 适配Java7语法 & Android API30环境
* 重构:抽离通用邮件发送逻辑,拆分业务函数
* 重构:抽离通用邮件发送逻辑,拆分业务函数,移除反射,适配邮件单例
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Date 2026-01-21 23:00:00
* @LastEditTime 2026/01/23 16:00:00
* @LastEditTime 2026/01/26 适配邮件单例+移除反射依赖
*/
public class AdminUtils {
// ========== 单例模式核心 ==========
@@ -102,47 +98,38 @@ public class AdminUtils {
);
}
// ========== 通用工具函数:发送自定义邮件 ==========
// ========== 通用工具函数:发送自定义邮件 核心调整 ==========
/**
* 通用邮件发送函数,所有通知函数复用此逻辑
* 适配单例、移除反射、优化初始化判断
* @param subject 邮件主题
* @param content 邮件正文内容
* @return 发送成功返回true失败返回false
*/
private boolean sendCustomEmail(String subject, String content) {
LogUtils.d(TAG, "sendCustomEmail() 函数调用,主题:" + subject);
EmailSendUtils emailUtils = EmailSendUtils.getInstance();
// 1. 初始化邮件配置
if (!EmailSendUtils.initEmailConfig()) {
// 1. 校验邮件工具是否就绪,未初始化则执行初始化
if (!emailUtils.isConfigInited()) {
LogUtils.d(TAG, "邮件工具未初始化,执行初始化流程");
boolean initOk = emailUtils.initEmailConfig();
if (!initOk) {
LogUtils.e(TAG, "邮件配置初始化失败,无法发送邮件");
return false;
}
}
// 2. 获取收件人邮箱
// 2. 获取收件人邮箱,统一用工具类校验空值
String sendToEmail = IniConfigUtils.getConfigValue("EmailConfig", "send_email_account", "");
if (EmailSendUtils.isStrEmpty(sendToEmail)) {
LogUtils.e(TAG, "收件人邮箱 send_email_account 未配置");
return false;
}
// 3. 获取邮件会话
Session mailSession = getMailSessionFromEmailSendUtils();
if (mailSession == null) {
LogUtils.e(TAG, "获取邮件会话失败");
return false;
}
// 4. 构建邮件
// 3. 调用邮件工具发送,无需反射获取会话,解耦依赖
try {
MimeMessage message = new MimeMessage(mailSession);
String fromNickname = IniConfigUtils.getConfigValue("EmailConfig", "from_nickname", "AuthCenter 系统");
message.setFrom(new InternetAddress(sendToEmail, fromNickname, "UTF-8"));
message.addRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress(sendToEmail));
message.setSubject(subject, "UTF-8");
message.setText(content, "UTF-8");
Transport.send(message);
LogUtils.i(TAG, "自定义邮件发送成功,收件人:" + sendToEmail);
return true;
return emailUtils.sendTextEmail(sendToEmail, subject, content);
} catch (Exception e) {
LogUtils.e(TAG, "自定义邮件发送失败", e);
return false;
@@ -183,7 +170,7 @@ public class AdminUtils {
return sendCustomEmail(tempSubject, fullContent);
}
// ========== 新增:对外暴露用户ID获取方法 ==========
// ========== 对外暴露用户ID获取方法 ==========
/**
* 获取运行用户ID供外部工具类调用
* @return 格式化的用户ID字符串
@@ -192,18 +179,7 @@ public class AdminUtils {
return getUserIdInternal();
}
// ========== 原有工具函数 ==========
private Session getMailSessionFromEmailSendUtils() {
try {
java.lang.reflect.Field field = EmailSendUtils.class.getDeclaredField("mailSession");
field.setAccessible(true);
return (Session) field.get(null);
} catch (Exception e) {
LogUtils.e(TAG, "反射获取邮件会话失败", e);
return null;
}
}
// ========== 工具函数 移除反射相关无用方法 ==========
private String getLocalHostIp() {
try {
InetAddress localHost = InetAddress.getLocalHost();

View File

@@ -13,7 +13,7 @@ import java.util.logging.Level;
* 新增每处理一个指令通过AdminUtils发送临时邮件通知
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Date 2026/01/14 00:00:00
* @LastEditTime 2026/01/23 17:00:00
* @LastEditTime 2026/01/26 适配邮件工具单例调用
*/
public class ConsoleInputUtils {
private static final String TAG = "ConsoleInputUtils";
@@ -185,19 +185,36 @@ public class ConsoleInputUtils {
/**
* 调整核心:适配邮件单例,优化流程+精准反馈
* 处理邮件自测指令,自动生成测试码,发送到发送方邮箱
*/
private static void handleSelfTestMailCmd() {
LogUtils.d(TAG, "【指令处理】执行selftestmail指令启动邮件自测流程");
System.out.println("\n🔍 开始邮件配置自测,正在初始化邮件服务...");
boolean initResult = EmailSendUtils.initEmailConfig();
EmailSendUtils emailUtils = EmailSendUtils.getInstance();
// 先判断是否已初始化,未初始化再执行,避免重复初始化
boolean initResult = emailUtils.isConfigInited() ? true : emailUtils.initEmailConfig();
if (!initResult) {
System.err.println("❌ 邮件配置初始化失败,自测终止!请先完善INI文件[EmailConfig]配置");
System.err.println("❌ 邮件配置初始化失败,自测终止!");
System.err.println("🔧 排查1.检查INI[EmailConfig]配置 2.确认send_email_account和smtp_auth_code必填项 3.核对邮箱SMTP权限");
LogUtils.w(TAG, "邮件初始化失败,自测流程终止");
return;
}
String testCode = String.valueOf((int)((Math.random() * 900000) + 100000));
EmailSendUtils.sendSelfTestEmail(testCode);
System.out.println("📝 生成自测校验码:" + testCode + ",正在发送自测邮件...");
boolean sendResult = emailUtils.sendSelfTestEmail(testCode);
if (sendResult) {
System.out.println("✅ 邮件自测成功!请前往发送方邮箱查收");
LogUtils.i(TAG, "邮件自测完成,测试码:"+testCode);
} else {
System.err.println("❌ 邮件自测失败!");
System.err.println("🔧 排查1.授权码有效性 2.网络连通性 3.javax.mail依赖完整性 4.端口465是否开放");
LogUtils.w(TAG, "邮件自测发送失败");
}
}
/**
@@ -245,6 +262,8 @@ public class ConsoleInputUtils {
System.out.println(" 日志级别:" + LogUtils.getGlobalLogLevel().getName());
System.out.println(" 服务器地址:" + ServerUtils.getServerUrl());
System.out.println(" 项目根目录:" + IniConfigUtils.getConfigValue("GlobalConfig", "PROJECT_ROOT_DIR", "【未设置】"));
// 新增邮件工具状态
System.out.println(" 邮件服务:" + (EmailSendUtils.getInstance().isConfigInited() ? "✅ 已初始化" : "❌ 未就绪"));
}
/**

View File

@@ -11,32 +11,57 @@ import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
/**
* 邮件发送工具类读取INI配置专注验证码发送场景兼容Android/JVM
* 邮件发送工具类(单例模式)读取INI配置专注验证码发送场景兼容Android/JVM
* 适配javax.mail依赖原生兼容Java7语法与Android API30环境
* 调整:仅保留smtp_auth_code配置读取移除send_email_auth_code相关逻辑
* 修复默认端口与SSL协议匹配
* 核心调整:改为线程安全懒汉单例+重整初始化逻辑+适配新测试用例
* 新增:公开静态空值校验方法 isStrEmpty供外部类调用
* 本次修复终极规避AWT依赖配合补丁类彻底解决java.awt.datatransfer.Transferable缺失异常
* 新增无参test()自动生成6位测试码快速自测邮件配置
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Date 2026-01-15 00:00:00
* @LastEditTime 2026-01-22 00:00:00
* @LastEditTime 2026-01-26 补全防AWT配置兼容android-30.jar不冲突
*/
public class EmailSendUtils {
private static final String TAG = "EmailSendUtils";
private static Properties mailProps = new Properties();
private static Session mailSession;
private static String fromEmail;
private static String fromNickname;
private static String emailSubjectPrefix;
private static String verifyCodeContentTemplate;
// 终极防AWT统一文本类型常量杜绝隐性触发AWT类
private static final String MAIL_CONTENT_TYPE_PLAIN = "text/plain;charset=UTF-8";
// ========== 单例核心(懒汉模式-线程安全) ==========
private static volatile EmailSendUtils instance = null;
// ========== 成员变量(单例私有,替代原静态变量) ==========
private Properties mailProps = new Properties();
private Session mailSession;
private String fromEmail;
private String fromNickname;
private String emailSubjectPrefix;
private String verifyCodeContentTemplate;
private boolean isConfigInited = false; // 配置初始化标识
// ========== 默认配置常量 ==========
private static final String DEFAULT_SMTP_HOST = "smtp.qq.com";
private static final String DEFAULT_SMTP_PORT = "465";
private static final String DEFAULT_SUBJECT_PREFIX = "【AuthCenter 认证中心】";
private static final String DEFAULT_CONTENT_TEMPLATE = "您的认证验证码为:{code}有效期5分钟请及时使用请勿泄露给他人";
private static final String CONFIG_SECTION_EMAIL = "EmailConfig";
// ========== 单例私有构造 ==========
private EmailSendUtils(){}
// ========== 获取单例实例(对外唯一入口) ==========
public static EmailSendUtils getInstance() {
if (instance == null) {
synchronized (EmailSendUtils.class) {
if (instance == null) {
instance = new EmailSendUtils();
}
}
}
return instance;
}
/**
* 【新增公开方法】单字符串空值校验,供外部类调用
* 【公开方法】单字符串空值校验,供外部类调用
* @param str 待校验字符串
* @return true=空/空白false=非空
*/
@@ -45,32 +70,48 @@ public class EmailSendUtils {
}
/**
* 初始化邮件配置
* 仅读取smtp_auth_code无其他兜底逻辑配置匹配更精准
* 【重整核心】初始化邮件配置(适配新测试用例,支持重复调用)
* 已初始化直接返回true未初始化则读取配置构建会话校验核心参数
* @return 配置完整且初始化成功返回true否则false
*/
public static boolean initEmailConfig() {
public boolean initEmailConfig() {
// 已初始化,直接返回成功
if (isConfigInited && mailSession != null) {
LogUtils.d(TAG, "邮件配置已初始化,无需重复执行");
return true;
}
LogUtils.d(TAG, "initEmailConfig 函数调用,开始初始化邮件配置");
// 仅读取smtp_auth_code删除send_email_auth_code相关代码
String smtpHost = IniConfigUtils.getConfigValue("EmailConfig", "smtp_host", DEFAULT_SMTP_HOST);
String smtpPort = IniConfigUtils.getConfigValue("EmailConfig", "smtp_port", DEFAULT_SMTP_PORT);
fromEmail = IniConfigUtils.getConfigValue("EmailConfig", "send_email_account", "");
final String smtpAuthCode = IniConfigUtils.getConfigValue("EmailConfig", "smtp_auth_code", "");
fromNickname = IniConfigUtils.getConfigValue("EmailConfig", "from_nickname", "");
emailSubjectPrefix = IniConfigUtils.getConfigValue("EmailConfig", "email_subject_prefix", DEFAULT_SUBJECT_PREFIX);
verifyCodeContentTemplate = IniConfigUtils.getConfigValue("EmailConfig", "verify_code_email_content", DEFAULT_CONTENT_TEMPLATE);
// 读取INI配置精简key名直接关联配置段
String smtpHost = IniConfigUtils.getConfigValue(CONFIG_SECTION_EMAIL, "smtp_host", DEFAULT_SMTP_HOST);
String smtpPort = IniConfigUtils.getConfigValue(CONFIG_SECTION_EMAIL, "smtp_port", DEFAULT_SMTP_PORT);
fromEmail = IniConfigUtils.getConfigValue(CONFIG_SECTION_EMAIL, "send_email_account", "");
final String smtpAuthCode = IniConfigUtils.getConfigValue(CONFIG_SECTION_EMAIL, "smtp_auth_code", "");
fromNickname = IniConfigUtils.getConfigValue(CONFIG_SECTION_EMAIL, "from_nickname", "");
emailSubjectPrefix = IniConfigUtils.getConfigValue(CONFIG_SECTION_EMAIL, "email_subject_prefix", DEFAULT_SUBJECT_PREFIX);
verifyCodeContentTemplate = IniConfigUtils.getConfigValue(CONFIG_SECTION_EMAIL, "verify_code_email_content", DEFAULT_CONTENT_TEMPLATE);
LogUtils.d(TAG, "读取INI邮件配置smtpHost[" + smtpHost + "] smtpPort[" + smtpPort + "] fromEmail[" + fromEmail + "]");
// 核心校验仅校验send_email_account和smtp_auth_code
// 核心校验:发送邮箱+授权码 必填
if (isEmpty(fromEmail)) {
LogUtils.e(TAG, "邮件核心配置缺失send_email_account 未配置");
resetConfig();
return false;
}
if (isEmpty(smtpAuthCode)) {
LogUtils.e(TAG, "邮件核心配置缺失smtp_auth_code 未配置");
resetConfig();
return false;
}
// 配置SMTP参数SSL适配固定协议兼容Java7+android-30
mailProps.clear();
// 核心新增3行防AWT配置配合补丁类彻底杜绝冲突
mailProps.put("mail.mime.multipart.ignoreexistingboundaryparameter", "true");
mailProps.put("mail.mime.base64.ignoreerrors", "true");
mailProps.put("mail.mime.encodefilename", "false");
// 原有核心配置
mailProps.put("mail.smtp.host", smtpHost);
mailProps.put("mail.smtp.port", smtpPort);
mailProps.put("mail.smtp.auth", "true");
@@ -79,6 +120,7 @@ public class EmailSendUtils {
mailProps.put("mail.transport.protocol", "smtp");
try {
// 创建邮件会话匿名内部类适配Java7
mailSession = Session.getInstance(mailProps, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
@@ -86,22 +128,37 @@ public class EmailSendUtils {
}
});
mailSession.setDebug(false);
LogUtils.i(TAG, "邮件发送配置初始化完成,发送方邮箱:" + fromEmail + " 发送方昵称:" + (fromNickname == null ? "默认昵称" : fromNickname));
isConfigInited = true; // 标记初始化完成
LogUtils.i(TAG, "邮件发送配置初始化完成,发送方邮箱:" + fromEmail + " 发送方昵称:" + (isEmpty(fromNickname) ? "默认昵称" : fromNickname));
return true;
} catch (Exception e) {
LogUtils.e(TAG, "邮件会话创建失败,检查配置是否正确", e);
resetConfig();
return false;
}
}
/**
* 单元测试专用函数
* @param testCode 测试验证码
* 【新增无参自测】自动生成6位测试码快速校验邮件配置最常用
* 无需传参,一键自测,适配快速验证场景
* @return 自测成功返回true失败返回false
*/
public boolean test() {
// 自动生成6位纯数字测试码和验证码规则一致
String testCode = String.valueOf((int)(Math.random() * 900000 + 100000));
LogUtils.d(TAG, "无参test()调用,自动生成测试码:" + testCode);
return test(testCode);
}
/**
* 单元测试专用函数(适配新测试用例,单例调用)
* @param testCode 自定义测试码
* @return 测试邮件发送成功返回true否则false
*/
public static boolean test(String testCode) {
public boolean test(String testCode) {
LogUtils.d(TAG, "test() 函数调用,开始邮件工具类单元测试");
if (mailSession == null) {
// 未初始化则自动执行,失败直接终止
if (!isConfigInited) {
LogUtils.w(TAG, "邮件会话未初始化,自动执行配置初始化");
boolean initResult = initEmailConfig();
if (!initResult) {
@@ -113,15 +170,15 @@ public class EmailSendUtils {
}
/**
* 发送验证码邮件
* 发送验证码邮件(核心业务方法)- 终极防AWT写法
* @param toEmail 接收方邮箱
* @param verifyCode 验证码
* @return 发送成功返回true失败返回false
*/
public static boolean sendVerifyCodeEmail(String toEmail, String verifyCode) {
public boolean sendVerifyCodeEmail(String toEmail, String verifyCode) {
LogUtils.d(TAG, "sendVerifyCodeEmail 函数调用参数toEmail[" + toEmail + "] verifyCode[" + verifyCode + "]");
if (mailSession == null) {
LogUtils.e(TAG, "邮件会话未初始化请先调用initEmailConfig()");
// 前置校验:配置+参数
if (!checkInitStatus()) {
return false;
}
if (isEmpty(toEmail, verifyCode)) {
@@ -132,11 +189,15 @@ public class EmailSendUtils {
try {
MimeMessage message = new MimeMessage(mailSession);
String finalNickname = isEmpty(fromNickname) ? fromEmail.split("@")[0] : fromNickname;
// 防乱码+防AWT
message.setFrom(new InternetAddress(fromEmail, finalNickname, "UTF-8"));
message.addRecipient(Message.RecipientType.TO, new InternetAddress(toEmail));
message.setSubject(emailSubjectPrefix + " 验证码通知", "UTF-8");
String content = verifyCodeContentTemplate.replace("{code}", verifyCode);
message.setText(content, "UTF-8");
// 终极写法1杜绝触发AWT类固定文本类型
message.setContent(content, MAIL_CONTENT_TYPE_PLAIN);
Transport.send(message);
LogUtils.i(TAG, "验证码邮件发送成功,收件人:" + toEmail);
return true;
@@ -147,19 +208,18 @@ public class EmailSendUtils {
}
/**
* 发送自测邮件到发送方自身邮箱
* 发送自测邮件到发送方自身邮箱 - 终极防AWT写法彻底解决报错
* @param testCode 自定义测试码
* @return 自测发送成功返回true失败返回false
*/
public static boolean sendSelfTestEmail(String testCode) {
public boolean sendSelfTestEmail(String testCode) {
LogUtils.d(TAG, "sendSelfTestEmail 函数调用,开始邮件自测,测试码:" + testCode);
if (isEmpty(fromEmail, testCode)) {
LogUtils.w(TAG, "发送方邮箱或测试码为空,自测失败");
// 前置校验:配置+测试码
if (!checkInitStatus()) {
return false;
}
if (mailSession == null) {
LogUtils.e(TAG, "邮件会话未初始化自测终止请先完成INI邮件配置");
System.err.println("❌ 邮件会话未初始化请先检查INI文件的[EmailConfig]配置项是否完整");
if (isEmpty(testCode)) {
LogUtils.w(TAG, "测试码为空,自测失败");
return false;
}
@@ -167,25 +227,59 @@ public class EmailSendUtils {
MimeMessage message = new MimeMessage(mailSession);
String finalNickname = isEmpty(fromNickname) ? fromEmail.split("@")[0] : fromNickname;
message.setFrom(new InternetAddress(fromEmail, finalNickname, "UTF-8"));
// 自测发往自身邮箱
message.addRecipient(Message.RecipientType.TO, new InternetAddress(fromEmail));
message.setSubject(emailSubjectPrefix + " 配置自测通知", "UTF-8");
String content = String.format("邮件发送配置自测成功!\n自测校验码%s\n当前发送方邮箱%s\n说明邮件服务配置正常可正常发送验证码。", testCode, fromEmail);
message.setText(content, "UTF-8");
// 终极写法2彻底规避AWT依赖无任何隐性调用
message.setContent(content, MAIL_CONTENT_TYPE_PLAIN);
Transport.send(message);
LogUtils.i(TAG, "自测邮件发送成功,已发送至发送方邮箱:" + fromEmail);
System.out.println("✅ 邮件自测成功,请到 " + fromEmail + " 查收测试邮件");
return true;
} catch (Exception e) {
LogUtils.e(TAG, "自测邮件发送失败,发送目标:" + fromEmail, e);
System.err.println("❌ 邮件自测失败请检查INI配置、邮箱授权码网络连接");
System.err.println("❌ 邮件自测失败请检查INI配置、邮箱授权码网络连接或依赖版本");
return false;
}
}
/**
* 【私有方法】多参数空值校验,内部使用
* 发送文本邮件(适配测试报告发送)- 终极防AWT写法
* @param toEmail 收件邮箱
* @param subject 邮件主题
* @param content 邮件内容
* @return 发送成功返回true
*/
private static boolean isEmpty(String... strs) {
public boolean sendTextEmail(String toEmail, String subject, String content) {
LogUtils.d(TAG, "sendTextEmail 函数调用,收件人:" + toEmail);
if (!checkInitStatus() || isEmpty(toEmail, subject, content)) {
return false;
}
try {
MimeMessage message = new MimeMessage(mailSession);
String finalNickname = isEmpty(fromNickname) ? fromEmail.split("@")[0] : fromNickname;
message.setFrom(new InternetAddress(fromEmail, finalNickname, "UTF-8"));
message.addRecipient(Message.RecipientType.TO, new InternetAddress(toEmail));
message.setSubject(subject, "UTF-8");
// 终极写法3统一标准杜绝AWT相关报错
message.setContent(content, MAIL_CONTENT_TYPE_PLAIN);
Transport.send(message);
LogUtils.i(TAG, "文本邮件发送成功,收件人:" + toEmail);
return true;
} catch (Exception e) {
LogUtils.e(TAG, "文本邮件发送失败,收件人:" + toEmail, e);
return false;
}
}
// ========== 私有辅助方法 ==========
/** 多参数空值校验,内部使用 */
private boolean isEmpty(String... strs) {
for (String str : strs) {
if (str == null || str.trim().isEmpty()) {
return true;
@@ -193,5 +287,27 @@ public class EmailSendUtils {
}
return false;
}
/** 检查初始化状态,未初始化则提示 */
private boolean checkInitStatus() {
if (!isConfigInited || mailSession == null) {
LogUtils.e(TAG, "邮件会话未初始化请先调用initEmailConfig()");
return false;
}
return true;
}
/** 初始化失败,重置配置状态 */
private void resetConfig() {
isConfigInited = false;
mailSession = null;
fromEmail = null;
fromNickname = null;
}
// ========== 对外提供初始化状态查询(适配测试用例调用) ==========
public boolean isConfigInited() {
return this.isConfigInited;
}
}

View File

@@ -3,107 +3,344 @@ package cc.winboll.util;
import cc.winboll.LogUtils;
/**
* 运行环境信息工具类,独立封装环境检测、信息获取逻辑
* 适配Java7,支持服务器/Android双环境参数化控制输出
* 运行环境信息工具类
* 适配Java7 + Android/Dalvik/ART/Termux OpenJDK多环境封装环境检测、信息获取、报告生成
* 修复点1.Android UID/品牌/型号/Java版本获取异常 2.Termux OpenJDK用户ID显示未知 3.系统指纹获取失败
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Date 2026/01/15
* @LastEdit 修复Termux用户ID未知问题+兼容多环境UID获取+统一异常处理逻辑
*/
public class EnvInfoUtils {
private static final String TAG = "EnvInfoUtils";
/**
* 核心方法:打印环境检测报告,支持参数控制是否输出详细信息
* @param showDetail true=显示完整报告false=仅显示核心信息
*/
public static void printEnvReport(boolean showDetail) {
if (!showDetail) {
printSimpleEnvInfo();
return;
}
printDetailedEnvInfo();
// ========== 系统属性常量(按类型归类) ==========
// Java核心属性
public static final String PROP_JAVA_VERSION = "java.version";
public static final String PROP_JAVA_VM_NAME = "java.vm.name";
public static final String PROP_JAVA_VM_VENDOR = "java.vm.vendor";
public static final String PROP_JAVA_HOME = "java.home";
public static final String PROP_JAVA_SPEC_VER = "java.specification.version";
// 操作系统属性
public static final String PROP_OS_NAME = "os.name";
public static final String PROP_OS_ARCH = "os.arch";
public static final String PROP_OS_VERSION = "os.version";
public static final String PROP_FILE_SEP = "file.separator";
public static final String PROP_PATH_SEP = "path.separator";
public static final String PROP_USER_ID = "user.id";// JVM专属Android/Termux不支持
// 运行目录&用户属性
public static final String PROP_USER_DIR = "user.dir";
public static final String PROP_USER_NAME = "user.name";
public static final String PROP_USER_HOME = "user.home";
public static final String PROP_FILE_ENCODING = "file.encoding";
// Android专属属性仅标记需反射获取
public static final String PROP_ANDROID_SDK = "android.os.Build.VERSION.SDK_INT";
public static final String PROP_ANDROID_MODEL = "android.os.Build.MODEL";
public static final String PROP_ANDROID_BRAND = "android.os.Build.BRAND";
public static final String PROP_ANDROID_FINGER = "android.os.Build.FINGERPRINT";
// ========== 对外核心API简洁统一 ==========
/** 获取单个环境属性值 */
public static String getEnvInfo(String key) {
return getSysProp(key, "未知");
}
/**
* 打印精简环境信息(快速启动场景)
*/
private static void printSimpleEnvInfo() {
System.out.println("===== 运行环境核心信息 =====");
System.out.println("Java版本: " + getSysProp("java.version", "未知"));
System.out.println("运行系统: " + getSysProp("os.name", "未知") + "(" + getSysProp("os.arch", "未知") + ")");
System.out.println("运行目录: " + getSysProp("user.dir", "未知"));
/** 获取核心环境标识快速区分Android/Termux/JVM */
public static String getEnvFlag() {
String javaVer = getAvailableJavaVersion();
if (isAndroidEnv()) {
System.out.println("运行环境: Android(" + getSysProp("os.version", "未知") + ")");
return "Android-" + getEnvInfo(PROP_OS_VERSION);
} else if (isTermuxEnv()) {
return "Termux-OpenJDK-" + javaVer;
} else {
return "JVM-" + javaVer;
}
}
/** 打印环境报告(支持精简/完整模式) */
public static void printEnvReport(boolean showDetail) {
preloadSysProps();
if (showDetail) {
printDetailedEnvReport();
} else {
printSimpleEnvReport();
}
}
/** 工具类自测入口(一键验证所有功能) */
public static void test() {
LogUtils.d(TAG, "EnvInfoUtils 自测启动");
System.out.println(">>>>>>>>>>>>> EnvInfoUtils 自测启动 <<<<<<<<<<<<<");
preloadSysProps();
// 核心标识
System.out.println("【核心环境标识】: " + getEnvFlag());
System.out.println("【是否Android环境】: " + (isAndroidEnv() ? "" : ""));
System.out.println("【是否Termux环境】: " + (isTermuxEnv() ? "" : ""));
// 打印双版本报告
System.out.println("\n--- 精简环境报告 ---");
printSimpleEnvReport();
System.out.println("--- 完整环境报告 ---");
printDetailedEnvReport();
// 单独属性演示
System.out.println(">>>>>>>>>>>>> 常用属性单独获取演示 <<<<<<<<<<<<<");
System.out.println("Java版本: " + getAvailableJavaVersion());
System.out.println("运行系统: " + getEnvInfo(PROP_OS_NAME));
System.out.println("运行目录: " + getEnvInfo(PROP_USER_DIR));
System.out.println("运行用户ID: " + getOsUserId());
System.out.println("设备品牌: " + getAndroidBrand());
System.out.println("设备型号: " + getAndroidModel());
System.out.println("系统指纹: " + getAndroidFingerprint());
System.out.println(">>>>>>>>>>>>> EnvInfoUtils 自测结束 <<<<<<<<<<<<<");
LogUtils.d(TAG, "EnvInfoUtils 自测完成");
}
/** 获取完整环境报告文本(供邮件/日志输出) */
public static String getFullEnvReportText() {
preloadSysProps();
StringBuilder sb = new StringBuilder();
sb.append("========================================").append("\n");
sb.append(" 运行环境详细检测报告").append("\n");
sb.append("========================================").append("\n\n");
// Java信息
sb.append("1. Java 核心信息").append("\n");
sb.append(" - Java 版本号: ").append(getAvailableJavaVersion()).append("\n");
sb.append(" - Java 虚拟机: ").append(getEnvInfo(PROP_JAVA_VM_NAME)).append("\n");
sb.append(" - VM 厂商信息: ").append(getEnvInfo(PROP_JAVA_VM_VENDOR)).append("\n");
sb.append(" - Java 环境路径: ").append(getEnvInfo(PROP_JAVA_HOME)).append("\n\n");
// 系统信息
sb.append("2. 操作系统信息").append("\n");
sb.append(" - 系统名称: ").append(getEnvInfo(PROP_OS_NAME)).append("\n");
sb.append(" - 系统架构: ").append(getEnvInfo(PROP_OS_ARCH)).append("\n");
sb.append(" - 系统版本: ").append(getEnvInfo(PROP_OS_VERSION)).append("\n");
sb.append(" - 文件分隔符: ").append(getEnvInfo(PROP_FILE_SEP)).append("\n");
sb.append(" - 路径分隔符: ").append(getEnvInfo(PROP_PATH_SEP)).append("\n");
sb.append(" - 运行用户ID: ").append(getOsUserId()).append("\n\n");
// 运行目录&用户
sb.append("3. 运行目录&用户信息").append("\n");
sb.append(" - 当前运行目录: ").append(getEnvInfo(PROP_USER_DIR)).append("\n");
sb.append(" - 当前用户名称: ").append(getEnvInfo(PROP_USER_NAME)).append("\n");
sb.append(" - 用户主目录: ").append(getEnvInfo(PROP_USER_HOME)).append("\n");
sb.append(" - 文件编码格式: ").append(getEnvInfo(PROP_FILE_ENCODING)).append("\n\n");
// Android专属
if (isAndroidEnv()) {
sb.append("4. Android 专属信息").append("\n");
sb.append(" - 设备品牌: ").append(getAndroidBrand()).append("\n");
sb.append(" - 设备型号: ").append(getAndroidModel()).append("\n");
sb.append(" - 系统指纹: ").append(getAndroidFingerprint()).append("\n");
}
sb.append("========================================");
return sb.toString();
}
// ========== 环境判断核心方法 ==========
/** 判断是否为Android环境Dalvik/ART虚拟机 */
public static boolean isAndroidEnv() {
String vmName = getEnvInfo(PROP_JAVA_VM_NAME);
return vmName.contains("Dalvik") || vmName.contains("ART");
}
/** 判断是否为Termux环境OpenJDK+Termux厂商标识 */
public static boolean isTermuxEnv() {
String vendor = getEnvInfo(PROP_JAVA_VM_VENDOR);
String javaHome = getEnvInfo(PROP_JAVA_HOME);
return vendor.contains("Termux") || javaHome.contains("com.termux");
}
// ========== 报告打印(精简+完整,消除重复) ==========
/** 打印精简报告 */
private static void printSimpleEnvReport() {
System.out.println("===== 运行环境核心信息 =====");
System.out.println("Java版本: " + getAvailableJavaVersion());
System.out.println("运行系统: " + getEnvInfo(PROP_OS_NAME) + "(" + getEnvInfo(PROP_OS_ARCH) + ")");
System.out.println("运行目录: " + getEnvInfo(PROP_USER_DIR));
System.out.println("运行用户ID: " + getOsUserId());
if (isAndroidEnv()) {
System.out.println("运行环境: Android(" + getEnvInfo(PROP_OS_VERSION) + ")");
System.out.println("设备品牌: " + getAndroidBrand());
System.out.println("设备型号: " + getAndroidModel());
} else if (isTermuxEnv()) {
System.out.println("运行环境: Termux OpenJDK");
} else {
System.out.println("运行环境: 标准JVM");
}
System.out.println("===========================\n");
}
/**
* 打印完整环境详细报告(调试/运维场景)
*/
private static void printDetailedEnvInfo() {
LogUtils.d(TAG, "开始执行完整运行环境检测");
/** 打印完整报告 */
private static void printDetailedEnvReport() {
LogUtils.d(TAG, "执行完整环境检测");
System.out.println("========================================");
System.out.println(" 运行环境详细检测报告");
System.out.println("========================================");
// Java核心信息
System.out.println("1. Java 核心信息");
System.out.println(" - Java 版本号: " + getSysProp("java.version", "未知版本"));
System.out.println(" - Java 虚拟机: " + getSysProp("java.vm.name", "未知虚拟机"));
System.out.println(" - VM 厂商信息: " + getSysProp("java.vm.vendor", "未知厂商"));
System.out.println(" - VM 版本号: " + getSysProp("java.vm.version", "未知版本"));
System.out.println(" - Java 环境路径: " + getSysProp("java.home", "未知路径"));
System.out.println(" - Java 规范版本: " + getSysProp("java.specification.version", "未知"));
System.out.println(" - Java 版本号: " + getAvailableJavaVersion());
System.out.println(" - Java 虚拟机: " + getEnvInfo(PROP_JAVA_VM_NAME));
System.out.println(" - VM 厂商信息: " + getEnvInfo(PROP_JAVA_VM_VENDOR));
System.out.println(" - Java 环境路径: " + getEnvInfo(PROP_JAVA_HOME));
// 操作系统信息
System.out.println("\n2. 操作系统信息");
System.out.println(" - 系统名称: " + getSysProp("os.name", "未知系统"));
System.out.println(" - 系统架构: " + getSysProp("os.arch", "未知架构"));
System.out.println(" - 系统版本: " + getSysProp("os.version", "未知版本"));
System.out.println(" - 文件分隔符: " + getSysProp("file.separator", "未知"));
System.out.println(" - 路径分隔符: " + getSysProp("path.separator", "未知"));
System.out.println(" - 系统名称: " + getEnvInfo(PROP_OS_NAME));
System.out.println(" - 系统架构: " + getEnvInfo(PROP_OS_ARCH));
System.out.println(" - 系统版本: " + getEnvInfo(PROP_OS_VERSION));
System.out.println(" - 文件分隔符: " + getEnvInfo(PROP_FILE_SEP));
System.out.println(" - 路径分隔符: " + getEnvInfo(PROP_PATH_SEP));
System.out.println(" - 运行用户ID: " + getOsUserId());
// 运行目录&用户信息
System.out.println("\n3. 运行目录&用户信息");
System.out.println(" - 当前运行目录: " + getSysProp("user.dir", "未知目录"));
System.out.println(" - 当前用户名称: " + getSysProp("user.name", "未知用户"));
System.out.println(" - 用户主目录: " + getSysProp("user.home", "未知目录"));
System.out.println(" - 文件编码格式: " + getSysProp("file.encoding", "UTF-8"));
System.out.println(" - 当前运行目录: " + getEnvInfo(PROP_USER_DIR));
System.out.println(" - 当前用户名称: " + getEnvInfo(PROP_USER_NAME));
System.out.println(" - 用户主目录: " + getEnvInfo(PROP_USER_HOME));
System.out.println(" - 文件编码格式: " + getEnvInfo(PROP_FILE_ENCODING));
// Android专属信息
if (isAndroidEnv()) {
LogUtils.d(TAG, "检测到Android环境,补充专属信息");
LogUtils.d(TAG, "补充Android专属信息");
System.out.println("\n4. Android 专属信息");
System.out.println(" - Android SDK 等级: " + getSysProp("android.os.Build.VERSION.SDK_INT", "未知"));
System.out.println(" - 设备型号: " + getSysProp("android.os.Build.MODEL", "未知"));
System.out.println(" - 设备品牌: " + getSysProp("android.os.Build.BRAND", "未知"));
System.out.println(" - 系统指纹: " + getSysProp("android.os.Build.FINGERPRINT", "未知"));
System.out.println(" - 设备品牌: " + getAndroidBrand());
System.out.println(" - 设备型号: " + getAndroidModel());
System.out.println(" - 系统指纹: " + getAndroidFingerprint());
}
System.out.println("========================================\n");
LogUtils.d(TAG, "完整运行环境检测完成");
LogUtils.d(TAG, "完整环境检测完成");
}
/**
* 判定是否为Android环境
*/
public static boolean isAndroidEnv() {
String vmName = getSysProp("java.vm.name", "");
return vmName.contains("Dalvik") || vmName.contains("ART");
}
/**
* 安全获取系统属性,封装异常处理
*/
public static String getSysProp(String key, String defaultValue) {
// ========== 内部工具方法(统一收口,消除冗余) ==========
/** 安全获取系统属性(统一异常处理 */
private static String getSysProp(String key, String defaultValue) {
try {
String value = System.getProperty(key);
return (value == null || value.trim().isEmpty()) ? defaultValue : value.trim();
} catch (Exception e) {
LogUtils.w(TAG, "获取系统属性[" + key + "]失败,使用默认值", e);
LogUtils.w(TAG, "获取属性[" + key + "]失败,使用默认值", e);
return defaultValue;
}
}
/** 预热核心属性,解决首次获取失败问题 */
private static void preloadSysProps() {
getSysProp(PROP_USER_ID, "未知");
getSysProp(PROP_USER_NAME, "未知");
getSysProp(PROP_JAVA_VM_NAME, "未知");
}
/** 获取可用的Java版本兼容多环境 */
private static String getAvailableJavaVersion() {
String javaVer = getEnvInfo(PROP_JAVA_VERSION);
if ("0".equals(javaVer) || "未知".equals(javaVer)) {
javaVer = getSysProp("java.vm.version", "未知Java版本");
}
return javaVer;
}
/** 跨环境获取用户ID核心修复支持Android/Termux/JVM */
private static String getOsUserId() {
// 1. Android环境 取应用UID
if (isAndroidEnv()) {
try {
return String.valueOf(android.os.Process.myUid());
} catch (Exception e) {
LogUtils.w(TAG, "获取Android UID失败", e);
}
}
// 2. Termux环境 优先取环境变量备选读uid_map文件
if (isTermuxEnv()) {
try {
// 优先读Termux USER_ID环境变量
String userId = System.getenv("USER_ID");
if (userId != null && !userId.trim().isEmpty()) {
return userId.trim();
}
// 备选方案读取proc文件获取真实UID
String uidPath = "/proc/self/uid_map";
java.io.File uidFile = new java.io.File(uidPath);
if (uidFile.exists() && uidFile.canRead()) {
java.io.BufferedReader br = new java.io.BufferedReader(new java.io.FileReader(uidFile));
String line = br.readLine();
br.close();
if (line != null && line.trim().length() > 0) {
String[] parts = line.split("\\s+");
if (parts.length >= 1 && !parts[0].trim().isEmpty()) {
return parts[0].trim();
}
}
}
} catch (Exception e) {
LogUtils.w(TAG, "获取Termux用户ID失败", e);
}
}
// 3. 兜底JVM标准属性 + 最终默认值兼容Termux显示
String jvmUid = getSysProp(PROP_USER_ID, "");
return jvmUid.isEmpty() ? getEnvInfo(PROP_USER_NAME) : jvmUid;
}
// ========== Android专属方法对外公开兼容Java7 ==========
/** 获取Android设备品牌 */
public static String getAndroidBrand() {
if (!isAndroidEnv()) {
return isTermuxEnv() ? "Termux 环境" : "非Android环境";
}
try {
Class<?> buildClz = Class.forName("android.os.Build");
return (String) buildClz.getField("BRAND").get(null);
} catch (Exception e) {
LogUtils.w(TAG, "获取Android品牌失败", e);
return "未知品牌";
}
}
/** 获取Android设备型号 */
public static String getAndroidModel() {
if (!isAndroidEnv()) {
return isTermuxEnv() ? "Termux 环境" : "非Android环境";
}
try {
Class<?> buildClz = Class.forName("android.os.Build");
String model = (String) buildClz.getField("MODEL").get(null);
return model == null || model.trim().isEmpty() ? "未知型号" : model.trim();
} catch (Exception e) {
LogUtils.w(TAG, "获取Android型号失败", e);
return "未知型号";
}
}
/** 获取Android系统指纹 */
public static String getAndroidFingerprint() {
if (!isAndroidEnv()) {
return isTermuxEnv() ? "Termux 环境" : "非Android环境";
}
try {
Class<?> buildClz = Class.forName("android.os.Build");
String fingerprint = (String) buildClz.getField("FINGERPRINT").get(null);
return fingerprint == null || fingerprint.trim().isEmpty() ? "未知指纹" : fingerprint.trim();
} catch (Exception e) {
LogUtils.w(TAG, "获取Android系统指纹失败", e);
return "未知指纹";
}
}
/** 获取Android应用UID对外公开 */
public static String getAndroidUserId() {
return getOsUserId();
}
}

View File

@@ -16,7 +16,7 @@ import java.util.Map;
* 适配Java7语法兼容Android API30环境新增服务器连通性一键测试 + 本地验证码发送能力
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
* @Date 2026/01/16 00:00:00
* @LastEditTime 2026/01/22 18:00:00
* @LastEditTime 2026/01/26 适配邮件工具单例调用
*/
public class ServerUtils {
// 基础属性
@@ -61,8 +61,20 @@ public class ServerUtils {
String trimEmail = email.trim();
// 2. 生成6位验证码依赖VerifyCodeUtils需确保该类存在
String verifyCode = VerifyCodeUtils.generateCode();
// 3. 直接调用邮件工具类发送
boolean sendResult = EmailSendUtils.sendVerifyCodeEmail(trimEmail, verifyCode);
// 核心调整适配EmailSendUtils单例先判就绪再发送
EmailSendUtils emailUtils = EmailSendUtils.getInstance();
if (!emailUtils.isConfigInited()) {
LogUtils.d(TAG, "邮件工具未初始化,先执行初始化流程");
boolean initSuccess = emailUtils.initEmailConfig();
if (!initSuccess) {
LogUtils.e(TAG, "邮件工具初始化失败,无法发送验证码");
return false;
}
}
// 3. 单例调用发送验证码邮件
boolean sendResult = emailUtils.sendVerifyCodeEmail(trimEmail, verifyCode);
// 4. 缓存验证码供后续校验依赖VerifyCodeCacheUtils
if (sendResult) {
VerifyCodeCacheUtils.putCode(trimEmail, verifyCode);

View File

@@ -0,0 +1,53 @@
package java.awt.datatransfer;
import java.io.Serializable;
// 补丁类补全所有缺失构造方法和属性彻底解决ActivationDataFlavor初始化问题
public class DataFlavor implements Serializable {
private static final long serialVersionUID = 1L;
public static final DataFlavor stringFlavor = new DataFlavor(String.class, "Plain String");
private final Class<?> representationClass;
private final String mimeType;
private final String humanPresentableName;
// 构造方法1Class + humanName
public DataFlavor(Class<?> representationClass, String humanPresentableName) {
this.representationClass = representationClass;
this.mimeType = "text/plain";
this.humanPresentableName = humanPresentableName;
}
// 构造方法2mimeType + humanName核心缺失的构造方法
public DataFlavor(String mimeType, String humanPresentableName) {
this.representationClass = String.class;
this.mimeType = mimeType;
this.humanPresentableName = humanPresentableName;
}
// 构造方法3完整参数版本兼容更多场景
public DataFlavor(String mimeType, String humanPresentableName, ClassLoader classLoader) {
this(mimeType, humanPresentableName);
}
public Class<?> getRepresentationClass() {
return representationClass;
}
public String getMimeType() {
return mimeType;
}
public String getHumanPresentableName() {
return humanPresentableName;
}
public boolean isMimeTypeEqual(String mimeType) {
return this.mimeType != null && this.mimeType.equalsIgnoreCase(mimeType);
}
public boolean isMimeTypeEqual(DataFlavor dataFlavor) {
return dataFlavor != null && isMimeTypeEqual(dataFlavor.getMimeType());
}
}

View File

@@ -0,0 +1,11 @@
package java.awt.datatransfer;
import java.io.IOException;
// 补丁类完整实现Transferable接口适配javax.activation的所有调用
public interface Transferable {
Object getTransferData(DataFlavor flavor) throws IOException, UnsupportedFlavorException;
DataFlavor[] getTransferDataFlavors();
boolean isDataFlavorSupported(DataFlavor flavor);
}

View File

@@ -0,0 +1,9 @@
// 配套异常类新建UnsupportedFlavorException.java
package java.awt.datatransfer;
public class UnsupportedFlavorException extends Exception {
public UnsupportedFlavorException(DataFlavor flavor) {
super(flavor != null ? flavor.getMimeType() : "Unsupported data flavor");
}
}