适配AIDE Pro编译器运行。添加单元测试邮件发送功能。添加系统信息发送。
This commit is contained in:
@@ -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>
|
||||
|
||||
12
README.md
12
README.md
@@ -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
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.
BIN
libs/javax.mail-api-1.4.7.jar
Normal file
BIN
libs/javax.mail-api-1.4.7.jar
Normal file
Binary file not shown.
@@ -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", "正在处理事务...");
|
||||
|
||||
|
||||
@@ -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 {
|
||||
// ========== 静态属性(常量在前 变量在后,顺序规整) ==========
|
||||
@@ -40,7 +40,7 @@ public class WinBoLL {
|
||||
// 解析启动参数 + 加载INI根配置
|
||||
MainUtils.parseArgs(args);
|
||||
LogUtils.d(TAG, "【参数信息】main(),传入启动参数:" + MainUtils.arrayToString(args));
|
||||
|
||||
|
||||
|
||||
// 读取项目根目录 初始化日志目录
|
||||
String projectRootDir = "./";
|
||||
@@ -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服务,绑定控制台重启实例
|
||||
*/
|
||||
|
||||
5
src/cc/winboll/auth/ActionUtils.java
Normal file
5
src/cc/winboll/auth/ActionUtils.java
Normal file
@@ -0,0 +1,5 @@
|
||||
package cc.winboll.auth;
|
||||
|
||||
public class ActionUtils
|
||||
{
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
5
src/cc/winboll/models/ActionModel.java
Normal file
5
src/cc/winboll/models/ActionModel.java
Normal file
@@ -0,0 +1,5 @@
|
||||
package cc.winboll.models;
|
||||
|
||||
public class ActionModel
|
||||
{
|
||||
}
|
||||
194
src/cc/winboll/models/UserModel.java
Normal file
194
src/cc/winboll/models/UserModel.java
Normal 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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
// ========== 核心修改:读取GlobalConfig的reports_path初始化报告路径 ==========
|
||||
private static void initReportPathFromIni() {
|
||||
try {
|
||||
// 读取核心配置
|
||||
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");
|
||||
|
||||
// 解析启动参数中的配置文件路径
|
||||
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;
|
||||
}
|
||||
}
|
||||
// 拼接完整报告文件路径,文件名带时间戳防重复
|
||||
REPORT_FILE = new File(REPORT_DIR_PATH, "AuthCenter_TestReport_" + MainUtils.getCurrentTime().replaceAll("[: ]", "_") + ".txt").getAbsolutePath();
|
||||
|
||||
// 自动创建目录(多级目录也能创建)
|
||||
new File(REPORT_DIR_PATH).mkdirs();
|
||||
LogUtils.d(TAG, "报告路径初始化完成:" + REPORT_FILE);
|
||||
LogUtils.d(TAG, "日志目录路径:" + LOG_DIR_PATH);
|
||||
} catch (Exception e) {
|
||||
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 {
|
||||
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()) {
|
||||
LogUtils.e(TAG, "【配置缺失】GlobalConfig小节必填项PROJECT_ROOT_DIR未配置,程序退出");
|
||||
System.err.println("❌ 配置缺失:GlobalConfig.PROJECT_ROOT_DIR 不能为空,请检查config.ini");
|
||||
System.exit(1);
|
||||
// 初始化邮件配置(确保配置生效)
|
||||
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⚠️ 测试报告邮件发送失败,请检查邮箱配置");
|
||||
}
|
||||
PROJECT_ROOT_DIR = PROJECT_ROOT_DIR.trim();
|
||||
|
||||
// 读取测试报告目录(可选,兜底为根目录下的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);
|
||||
} catch (Exception e) {
|
||||
LogUtils.e(TAG, "【函数异常】加载外部配置失败,程序退出", e);
|
||||
System.err.println("❌ 配置文件加载失败:" + CONFIG_FILE_PATH);
|
||||
System.exit(1);
|
||||
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专项测试用例(最前面执行) ==========
|
||||
// 用例01:LogUtils基础分级日志输出测试
|
||||
// ========== 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
// 用例02:LogUtils异常堆栈打印测试
|
||||
// 新增用例02:EnvInfoUtils环境检测工具测试(紧跟核心工具类)
|
||||
testCaseList.add(new TestCase(
|
||||
"LogUtils异常日志打印测试",
|
||||
"验证LogUtils带异常堆栈的日志输出功能",
|
||||
"EnvInfoUtils环境检测测试",
|
||||
"验证环境信息工具类核心功能,输出环境标识+完整环境报告",
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// 构造测试异常
|
||||
Exception testException = new RuntimeException("LogUtils异常打印测试专用异常");
|
||||
LogUtils.e(TAG, "LogUtils带异常堆栈的日志测试", testException);
|
||||
testCaseList.get(1).isPass = true;
|
||||
LogUtils.d(TAG, "【用例结果】LogUtils异常日志打印测试通过");
|
||||
// 执行工具类自测
|
||||
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, "【用例结果】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开始)
|
||||
// 用例03:help指令响应测试
|
||||
// 用例03:help指令响应测试(索引同步后移)
|
||||
testCaseList.add(new TestCase(
|
||||
"help指令响应测试",
|
||||
"验证help指令能否正常打印帮助信息",
|
||||
@@ -273,33 +272,45 @@ public class ConsoleCmdAutoTest {
|
||||
}
|
||||
));
|
||||
|
||||
// 用例04:selftestmail指令测试 - 核心修改:调用 EmailSendUtils.test() 函数
|
||||
// 用例04:selftestmail指令测试 - 用无参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);
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
// 用例05:clearverifycode指令测试
|
||||
// 用例05:clearverifycode指令测试(索引同步后移)
|
||||
testCaseList.add(new TestCase(
|
||||
"clearverifycode指令测试",
|
||||
"验证清空验证码指令能否清空MailAuthUtils缓存",
|
||||
@@ -324,7 +335,7 @@ public class ConsoleCmdAutoTest {
|
||||
}
|
||||
));
|
||||
|
||||
// 用例06:testserver指令测试
|
||||
// 用例06:testserver指令测试(索引同步后移)
|
||||
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");
|
||||
|
||||
@@ -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()) {
|
||||
LogUtils.e(TAG, "邮件配置初始化失败,无法发送邮件");
|
||||
return false;
|
||||
// 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();
|
||||
|
||||
@@ -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() ? "✅ 已初始化" : "❌ 未就绪"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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,29 +120,45 @@ public class EmailSendUtils {
|
||||
mailProps.put("mail.transport.protocol", "smtp");
|
||||
|
||||
try {
|
||||
// 创建邮件会话(匿名内部类,适配Java7)
|
||||
mailSession = Session.getInstance(mailProps, new Authenticator() {
|
||||
@Override
|
||||
protected PasswordAuthentication getPasswordAuthentication() {
|
||||
return new PasswordAuthentication(fromEmail, smtpAuthCode);
|
||||
}
|
||||
});
|
||||
@Override
|
||||
protected PasswordAuthentication getPasswordAuthentication() {
|
||||
return new PasswordAuthentication(fromEmail, smtpAuthCode);
|
||||
}
|
||||
});
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 核心信息
|
||||
// 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 专属信息
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
// 基础属性
|
||||
@@ -53,17 +53,29 @@ public class ServerUtils {
|
||||
*/
|
||||
public static boolean sendVerifyCodeLocal(String email) {
|
||||
LogUtils.d(TAG, "调用sendVerifyCodeLocal,本地发送验证码,邮箱:" + email);
|
||||
// 1. 前置校验:邮箱格式
|
||||
// 1. 前置校验:邮箱格式
|
||||
if (email == null || email.trim().isEmpty() || !email.contains("@")) {
|
||||
LogUtils.w(TAG, "本地发送验证码失败:邮箱为空或格式不合法");
|
||||
return false;
|
||||
}
|
||||
String trimEmail = email.trim();
|
||||
// 2. 生成6位验证码(依赖VerifyCodeUtils,需确保该类存在)
|
||||
// 2. 生成6位验证码(依赖VerifyCodeUtils,需确保该类存在)
|
||||
String verifyCode = VerifyCodeUtils.generateCode();
|
||||
// 3. 直接调用邮件工具类发送
|
||||
boolean sendResult = EmailSendUtils.sendVerifyCodeEmail(trimEmail, verifyCode);
|
||||
// 4. 缓存验证码(供后续校验,依赖VerifyCodeCacheUtils)
|
||||
|
||||
// 核心调整:适配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);
|
||||
LogUtils.i(TAG, "本地验证码发送成功,邮箱[" + trimEmail + "] 验证码[" + verifyCode + "]");
|
||||
|
||||
53
src/java/awt/datatransfer/DataFlavor.java
Normal file
53
src/java/awt/datatransfer/DataFlavor.java
Normal 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;
|
||||
|
||||
// 构造方法1:Class + humanName
|
||||
public DataFlavor(Class<?> representationClass, String humanPresentableName) {
|
||||
this.representationClass = representationClass;
|
||||
this.mimeType = "text/plain";
|
||||
this.humanPresentableName = humanPresentableName;
|
||||
}
|
||||
|
||||
// 构造方法2:mimeType + 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());
|
||||
}
|
||||
}
|
||||
|
||||
11
src/java/awt/datatransfer/Transferable.java
Normal file
11
src/java/awt/datatransfer/Transferable.java
Normal 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);
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user