java 发送邮件功能
文章目录
- 一、项目背景
- 二、页面配置
- 三、注意点总结
- 四、邮箱收件效果
- 五、代码
- pom.xml
- ConfigValue.java 获取配置文件参数内容
- EmailSender.java
- EmailHelper.java
- DecryptionManager.java 授权码加密工具
- application.properties 配置文件
- messages_zh_CN.properties 词条
- 照片静态资源存放路径
一、项目背景
java+mysql+springboot+QQ企业邮箱作为发送方,项目监控中转台上下限状态改变,以及中转台上报告警信息时发邮件通知。
二、页面配置
三、注意点总结
注意点1:
页面参数说明:
邮箱地址
:发送方邮件地址密码
:授权码SMTP邮件服务器
:smtp.exmail.qq.comSMTP邮件服务器端口
:25/465SSL加密
:是/否
如果“SSL加密”,那么端口必须设置成465;如果“SSL不加密”,那么端口必须设置成25;否则发送邮件会报错,不适配。
注意点2:
邮箱配置信息主要在EmailHelper类中定义,sendEmail()方法定义配置信息。
EmailHelper
:初始化邮件发送对象EmailSender
:配置邮箱参数(标头、内容、图片等等)
四、邮箱收件效果
五、代码
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
ConfigValue.java 获取配置文件参数内容
public String obtainConfigValue(String key) {
String configValue = "";
try {
configValue = env.getProperty(key);
} catch (Exception ex) {
logger.error("[XNMSProxyService] ObtainConfigValue :" + ex.getMessage(), ex);
}
return configValue;
}
EmailSender.java
package com.xnms.service.center.utility;
import com.xnms.data.contract.Utility;
import com.xnms.data.contract.database.db.Repeater;
import com.xnms.data.contract.database.db.UserData;
import com.xnms.data.contract.security.DecryptionManager;
import com.xnms.data.contract.util.I18NService;
import com.xnms.service.center.model.ClientCallbackObject;
import com.xnms.service.center.security.SecurityControl;
import com.xnms.service.center.service.impl.data.castle.ProxyServCastle;
import com.xnms.service.center.service.impl.data.castle.data.server.call.DataServCallInvoker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Queue;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@Component
public class EmailSender implements InitializingBean {
private static Logger logger = LoggerFactory.getLogger(EmailSender.class);
private static final SimpleDateFormat yyyyMMddHHmmss = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
@Autowired
private DataServCallInvoker dataServCallInvoker;
@Autowired
private SecurityControl securityControl;
@Autowired
private ProxyServCastle proxyServCastle;
@Autowired
private I18NService i18NService;
@Autowired
private ConfigValue configValue;
private Thread sendEmailThread;
private Queue<EmailInfo> clientCallbackObjectQueue = new LinkedList<>();
private static String IMAGE_PATH = "\\..\\xnms-app\\config\\";
public EmailSender() {
logger.info("EmailSender constructor");
}
@Override
public void afterPropertiesSet() throws Exception {
sendEmailThread = new Thread(this::sendEmailThreadFunc);
sendEmailThread.setDaemon(true);
sendEmailThread.start();
}
private void sendEmailThreadFunc() {
try {
EmailInfo emailInfoInstance = null;
while (true) {
if (clientCallbackObjectQueue.isEmpty()) {
Thread.sleep(10);
continue;
}
synchronized (clientCallbackObjectQueue) {
emailInfoInstance = clientCallbackObjectQueue.poll();
}
if (emailInfoInstance == null || emailInfoInstance.getSendInfo() == null) {
continue;
}
if (emailInfoInstance.getInfoType() == EnumAlarmType.Alarm) {
sendAlarmEmailRealtime(emailInfoInstance.getSendInfo(), emailInfoInstance.getAlarmDateTime());
} else if (emailInfoInstance.getInfoType() == EnumAlarmType.OnOffLine) {
sendStateEmailRealtime(emailInfoInstance.getSendInfo(), emailInfoInstance.getAlarmDateTime());
}
logger.debug("<sendEmailThreadFunc> List Count[{}].", clientCallbackObjectQueue.size());
Thread.sleep(100);
}
} catch (Exception ex) {
logger.error("<EmailSender> sendEmailThreadFunc Thread is exit! ErrorCode: {}", ex.getMessage(), ex);
}
}
/**
* Send alarm email.
*
* @param cbo Client callback object
* @param alarmType Type of the alarm
*/
public void sendAlarmEmail(ClientCallbackObject cbo, EnumAlarmType alarmType) {
try {
if (!securityControl.obtainEmailJurisdiction()) {return;}
synchronized (clientCallbackObjectQueue) {
clientCallbackObjectQueue.add(new EmailInfo(alarmType, new Date(), cbo));
}
} catch (Exception ex) {
logger.error("<EmailSender> sendAlarmEmail Enqueue fail! ErrorCode:" + ex.getMessage(), ex);
}
}
//public enum ClientAlarmEnum
//{
// /// 电压(0-正常,1-低压告警 ,2-高压告警,3-电压异常_异常仅供rd960使用)
// Voltage, // 1.3.6.1.4.1.40297.1.2.1.1.1;
// /// 温度(0-正常,1-低温告警,目前不支持低温告警,2-高温告警)
// Temperature, // 1.3.6.1.4.1.40297.1.2.1.1.2;
// //Fan , // 1.3.6.1.4.1.40297.1.2.1.1.3;
// //Forward , // 1.3.6.1.4.1.40297.1.2.1.1.4;
// //Reflected , // 1.3.6.1.4.1.40297.1.2.1.1.5;
// /// 电压驻波比(0-正常,1-告警)
// Vswr, // 1.3.6.1.4.1.40297.1.2.1.1.6;
// /// 接收失锁(0-正常,1-告警)
// RxPll, // 1.3.6.1.4.1.40297.1.2.1.1.8;
// /// 发射失锁(0-正常,1-告警)
// TxPll, // 1.3.6.1.4.1.40297.1.2.1.1.7;
//}
// 发送告警邮件
public void sendAlarmEmailRealtime(ClientCallbackObject cbo, Date alarmTime) {
try {
Repeater repeater = dataServCallInvoker.getSyncCase().getRepeater(cbo.getInfokey().toString());
List<UserData> user = dataServCallInvoker.getSyncCase().getUsersByWarning(
cbo.getInfokey().toString(),
"",
Integer.toString(((Utility.EnumRptRunState) cbo.getTag()).ordinal() - 1)
);
if (user == null || user.isEmpty()) {
logger.debug("<SendAlarmEmail> Get nothing from GetUsersByWarning() for infoKey({}), Tag {}", cbo.getInfokey(), cbo.getInfovalue());
return;
}
// 0表示正常,不需要发送告警
// >7表示未定义告警类型
if ((int) cbo.getInfovalue() <= 0) {
logger.debug("<SendAlarmEmail> no alarm mail need to be sent");
return;
}
String mailFrom = configValue.obtainConfigValue("xnms.service.center.MailFrom");
String mailPwd = configValue.obtainConfigValue("xnms.service.center.MailPwd");
String mailHost = configValue.obtainConfigValue("xnms.service.center.MailHost");
Integer smtpPort = Integer.valueOf(configValue.obtainConfigValue("xnms.service.center.SmtpPort"));
Integer enableSsl = Integer.valueOf(configValue.obtainConfigValue("xnms.service.center.EnableSsl"));
if (mailFrom == null || mailFrom.trim().isEmpty() ||
mailPwd == null || mailPwd.trim().isEmpty() || smtpPort == null || enableSsl == null ||
mailHost == null || mailHost.trim().isEmpty()) {
return;
}
EmailHelper email = new EmailHelper();
email.setMailFrom(mailFrom);
email.setMailPwd(passwordChange());
email.setHost(mailHost);
email.setMailCcArray(new String[]{});
email.setBodyHtml(true);
email.setEnableSsl(enableSsl);
email.setSmtpPort(smtpPort);
Map<String, List<String>> emails = getToMail(user);
if (emails == null || emails.isEmpty()) {
logger.debug("<ClientCallbackThread SendAlarmEmail> no receive mailbox");
return;
}
for (String key : emails.keySet()) {
sendAlarmOneLanguage(cbo, email, repeater, emails.get(key), key, alarmTime);
TimeUnit.MILLISECONDS.sleep(100);
}
} catch (Exception ex) {
logger.error("<XNMSProxyService CallBackThread> " + ex.getMessage(), ex);
}
}
/**
* 根据用户信息分析邮箱地址和邮件语言
* @param users 用户列表
* @return 邮件语言和对应邮箱地址的映射
*/
private static Map<String, List<String>> getToMail(List<UserData> users) {
Map<String, List<String>> dicMailsToLanguage = new HashMap<>();
for (UserData user : users) {
// 邮箱地址合法
if (user.getEmail() != null && !user.getEmail().trim().isEmpty() && user.getEmail().contains("@") && user.getIsNeedEmail() == 1) {
String language = user.getNoticeLanguage().trim();
int languageInt = -1;
try {
languageInt = Integer.parseInt(language);
} catch (Exception e) {
languageInt = 1; // 默认值
}
String languageName = new Language().getLanguage().get(languageInt).getName();
String email = user.getEmail().trim();
if (dicMailsToLanguage.containsKey(languageName)) {
dicMailsToLanguage.get(languageName).add(email);
} else {
List<String> myMail = new ArrayList<>();
myMail.add(email);
dicMailsToLanguage.put(languageName, myMail);
}
}
}
if (dicMailsToLanguage.isEmpty()) {
return null;
}
return dicMailsToLanguage;
}
/**
* 密码解密
* @return 解密后的密码
*/
private String passwordChange() {
String password = configValue.obtainConfigValue("xnms.service.center.MailPwd");
logger.info("<XNMSProxyService SendAlarmEmailRealtime> password change before: {}", password);
try {
password = DecryptionManager.decryptLoginInfo(password);
} catch (Exception ex) {
password = "";
logger.error("PasswordChange: ", ex);
}
logger.info("<XNMSProxyService SendAlarmEmailRealtime> password change after: {}", password);
return password;
}
/**
* 分析告警信息
* @param cbo 上报信息
* @param alarmType 告警类型
* @param type 指标
* @param mailBody 用户文字提示
* @param imageName 用户图片提示
* @param language 语言
*/
private void getAlarmType(ClientCallbackObject cbo, String[] alarmType, String[] type, List<String> mailBody, List<String> imageName, String language) {
Utility.EnumRptRunState enumRptRunState = (Utility.EnumRptRunState) cbo.getTag();
mailBody.clear(); // 清空列表
imageName.clear(); // 清空列表
alarmType[0] = ""; // 重置告警类型
type[0] = ""; // 重置指标
switch (enumRptRunState) {
case Unknown:
alarmType[0] = "UnknownAlarm";
type[0] = "UnknownType";
break;
case Voltage:
if ((int) cbo.getInfovalue() == 1) {
alarmType[0] = "Alaram_voltageAlarm_Low";
mailBody.add(getAlarmString(language, "voltage1").replace("\n", "<br/>"));
mailBody.add(getAlarmString(language, "voltage2_Low").replace("\n", "<br/>"));
mailBody.add(getAlarmString(language, "voltage3").replace("\n", "<br/>"));
imageName.add("LowVoltageImage.png");
} else if ((int) cbo.getInfovalue() == 2) {
alarmType[0] = "Alaram_voltageAlarm_high";
mailBody.add(getAlarmString(language, "voltage1").replace("\n", "<br/>"));
mailBody.add(getAlarmString(language, "voltage2_High").replace("\n", "<br/>"));
mailBody.add(getAlarmString(language, "voltage3").replace("\n", "<br/>"));
imageName.add("HighVoltageImage.png");
} else {
alarmType[0] = "Alaram_batteryVoltageAlarm";
mailBody.add(getAlarmString(language, "voltage1").replace("\n", "<br/>"));
mailBody.add(getAlarmString(language, "voltage2_High").replace("\n", "<br/>"));
mailBody.add(getAlarmString(language, "voltage2_Low").replace("\n", "<br/>"));
mailBody.add(getAlarmString(language, "voltage3").replace("\n", "<br/>"));
imageName.add("HighVoltageImage.png");
imageName.add("LowVoltageImage.png");
}
type[0] = "Voltage";
break;
case Temperature:
if ((int) cbo.getInfovalue() == 1) {
alarmType[0] = "Alaram_temperatureAlarm_low";
} else {
alarmType[0] = "Alaram_temperatureAlarm_high";
}
type[0] = "Temperature";
mailBody.add(getAlarmString(language, "Temperature1").replace("\n", "<br/>"));
mailBody.add(getAlarmString(language, "Temperature2").replace("\n", "<br/>"));
imageName.add("temperatureImage.png");
break;
case Vswr:
if ((int) cbo.getInfovalue() == 1) {
alarmType[0] = "Alaram_vswrAlarm";
}
type[0] = "Vswr";
mailBody.add(getAlarmString(language, "Vswr1").replace("\n", "<br/>"));
mailBody.add(getAlarmString(language, "Vswr2").replace("\n", "<br/>"));
imageName.add("vswrImage.png");
break;
case TxPll:
if ((int) cbo.getInfovalue() == 1) {
alarmType[0] = "Alaram_txPllAlarm";
}
type[0] = "TxPll";
mailBody.add(getAlarmString(language, "TXUnlock1").replace("\n", "<br/>"));
mailBody.add(getAlarmString(language, "TXUnlock2").replace("\n", "<br/>"));
mailBody.add(getAlarmString(language, "RXTXUnlock3").replace("\n", "<br/>"));
imageName.add("TxPllImage.png");
break;
case RxPll:
if ((int) cbo.getInfovalue() == 1) {
alarmType[0] = "Alaram_rxPllAlarm";
}
type[0] = "RxPll";
mailBody.add(getAlarmString(language, "RXUnlock1").replace("\n", "<br/>"));
mailBody.add(getAlarmString(language, "RXUnlock2").replace("\n", "<br/>"));
mailBody.add(getAlarmString(language, "RXTXUnlock3").replace("\n", "<br/>"));
imageName.add("RxPllImage.png");
break;
case RxInterfereDetect:
if ((int) cbo.getInfovalue() == 1) {
alarmType[0] = "Alaram_RxinterferedectecAlarm";
}
type[0] = "RxInterferedectect";
break;
case TxInterfereDetect:
if ((int) cbo.getInfovalue() == 1) {
alarmType[0] = "Alaram_TxinterferedectecAlarm";
}
type[0] = "TxInterferedectect";
break;
default:
break;
}
}
// 发送某一种语言的告警邮件
private void sendAlarmOneLanguage(ClientCallbackObject cbo, EmailHelper email, Repeater repeater, List<String> emails, String language, Date alarmTime) throws CloneNotSupportedException {
email.getAttachments().clear();
email.setMailToArray(emails);
// 中转台别名
String alias = "";
// 中转台
String sn = cbo.getInfokey().toString();
// 中转台ID
String radioID = "";
// 站点ID
String siteID = "";
// 站点别名
String siteAlias = "";
if (repeater != null) {
alias = repeater.getAlias();
radioID = repeater.getRadioId().toString();
siteID = repeater.getSiteId().toString();
siteAlias = repeater.getSiteAlias();
}
// 邮件内容
String[] alarmType = new String[1];
String[] type = new String[1];
List<String> mailBody = new ArrayList<>();
List<String> imageName = new ArrayList<>();
getAlarmType(cbo, alarmType, type, mailBody, imageName, language);
// 添加附件
for (String image : imageName) {
//todo 资源的目录位置,这个先在都不知道逻辑关联,先标记
String name = String.format("%s/Image/%s/%s", System.getProperty("user.dir") + IMAGE_PATH, language, image);
//todo 可以定义一个附件类,先标记
Attachment attachment = new Attachment(name, UUID.randomUUID().toString());
;
email.getAttachments().add(attachment);
}
String alarmName = getAlarmString(language, alarmType[0]);
if (proxyServCastle.obtainProxyServInfo().getSystemType() == Utility.EnumSystemType.Conventional) {
//标题
//XPT信道机XXX告警!请尽快处理
//XNMS系统 站点别名:{0} 站点ID:{1} 中转台别名:{2} 中转台SN:{3}出现{4}!请尽快处理
//XNMS system site alias:{0} site ID:{1} repeater alias:{2} repeater SN:{3} appears {4}! Please handle as soon as possible
email.setMailSubject(MessageFormat.format(getAlarmString(language, "EmailTitleForConventional") + " " + yyyyMMddHHmmss.format(alarmTime), alias , sn , alarmName));
} else {
// 标题
//XPT信道机XXX告警!请尽快处理
//XNMS系统 站点别名:{0} 站点ID:{1} 中转台别名:{2} 中转台SN:{3}出现{4}!请尽快处理
//XNMS system site alias:{0} site ID:{1} repeater alias:{2} repeater SN:{3} appears {4}! Please handle as soon as possible
email.setMailSubject(MessageFormat.format(getAlarmString(language, "EmailTitle") + " " + yyyyMMddHHmmss.format(alarmTime), siteAlias , siteID , alias , sn , alarmName));
}
// 内容
//"XPTxxx(中转台别名以及ID)出现xxx告警,xxx指标出现故障,请尽快处理
//中转台 别名:{0} SN:{1} 出现{2},{3}指标出现故障请尽快处理
//Repeater alias:{0} SN:{1} appears {2}, {3} indicators of failure, please deal with as soon as possible
email.setMailBody(String.format("<p>%s</p>", String.format(MessageFormat.format(getAlarmString(language, "EmailConclusion"), alias, sn, alarmName, getAlarmString(language, type[0])))));
int currentLocation;
String mailBodyText = "";
for (currentLocation = 0; currentLocation < imageName.size(); currentLocation++) {
mailBodyText += mailBody.get(currentLocation);
//todo 这块我的附件id是啥意思,我自己定义了一个,先标记一下
mailBodyText += String.format("<br/><img src=\"cid:%s\"/><br/>", email.getAttachments().get(currentLocation).getId());
}
for (int j = currentLocation; j < mailBody.size(); j++) {
mailBodyText += mailBody.get(j);
}
email.setMailBody(mailBodyText);
// 发送
//todo 看看这块是否要填写相应的参数
if (email.sendEmail()) {
logger.debug("<XNMSProxyService CallBackThread> send email success. Info{}", email.getMailSubject());
} else {
logger.debug("<XNMSProxyService CallBackThread> send email fail. Info{}", email.getMailSubject());
}
}
/**
* 当前语言
* @param language language
* @param key key
* @return 结果
*/
public String getAlarmString(String language, String key) {
String value = "";
try {
// 假设 Language 是一个自定义类
Language languageObj = new Language();
// 获取语言列表
List<Language> languages = languageObj.getLanguage();
// 查找语言代码
Optional<Language> foundLanguage = languages.stream()
.filter(a -> a.getName().equals(language))
.findFirst();
if (foundLanguage.isPresent()) {
int languageCode = foundLanguage.get().getCode();
String zhCn = i18NService.getMessage("zh_CN", key);
switch (languageCode) {
// 中文
case 0:
if (i18NService.getMessage("zh_CN", key) != null && !i18NService.getMessage("zh_CN", key).isEmpty()) {
//value = LanguageFind.getInstance().getZhCN().get(key);
value = i18NService.getMessage("zh_CN",key);
} else {
value = ""; // 或者可以用 null
}
break;
// 其他语言(如英文)
default:
if (i18NService.getMessage("en_US", key) != null && !i18NService.getMessage("en_US", key).isEmpty()) {
//value = LanguageFind.getInstance().getEnUS().get(key);
value = i18NService.getMessage("en_US",key);
} else {
value = ""; // 或者可以用 null
}
break;
}
}
} catch (Exception ex) {
logger.error("<XNMSProxyService getAlarmString> " + ex.getMessage(), ex);
value = ""; // 或者可以用 null
}
return value;
}
/// 当前图片资源
/// <param name="key"></param>
/// <returns></returns>
public String getAlarmImage(String language, String key) {
// 获取图片
try {
String imagePath = configValue.obtainConfigValue("xnms.service.center.forwardImage");
// 从路径读取 BufferedImage
BufferedImage img = ImageIO.read(new File(imagePath));
// 将图片保存到字节流
ByteArrayOutputStream stream = new ByteArrayOutputStream();
ImageIO.write(img, "bmp", stream);
// 获取字节数组
byte[] arr = stream.toByteArray();
stream.close();
// 转换为 Base64 字符串
String strBase64 = Base64.getEncoder().encodeToString(arr);
return strBase64;
} catch (IOException e) {
logger.error("EmailSender类,getAlarmImage方法:", e);
return null;
}
}
// 发送中转台上下线邮件
private void sendStateEmailRealtime(ClientCallbackObject cbo, Date alarmTime) {
try {
String mailFrom = configValue.obtainConfigValue("xnms.service.center.MailFrom");
String mailPwd = configValue.obtainConfigValue("xnms.service.center.MailPwd");
String mailHost = configValue.obtainConfigValue("xnms.service.center.MailHost");
Integer smtpPort = Integer.valueOf(configValue.obtainConfigValue("xnms.service.center.SmtpPort"));
Integer enableSsl = Integer.valueOf(configValue.obtainConfigValue("xnms.service.center.EnableSsl"));
if (mailFrom == null || mailFrom.trim().isEmpty() ||
mailPwd == null || mailPwd.trim().isEmpty() || smtpPort == null || enableSsl == null ||
mailHost == null || mailHost.trim().isEmpty()) {
logger.debug("<ClientCallbackThread SendAlarmEmail> no send mailbox or no smtp mail server");
return;
}
EmailHelper email = new EmailHelper();
email.setMailFrom(mailFrom);
email.setMailPwd(passwordChange());
email.setHost(mailHost);
email.setMailCcArray(new String[]{});
email.setBodyHtml(false);
email.setEnableSsl(enableSsl);
email.setSmtpPort(smtpPort);
Repeater repeater = dataServCallInvoker.getSyncCase().getRepeater(cbo.getInfokey().toString());
List<UserData> users = dataServCallInvoker.getSyncCase().getUsersByRepeaterSN(cbo.getInfokey().toString());
Map<String, List<String>> emails = getToMail(users);
if (emails == null || emails.isEmpty()) {
logger.debug("<ClientCallbackThread SendAlarmEmail> no receive mailbox");
return;
}
for (String key : emails.keySet()) {
sendStateOneLanguage(cbo, email, repeater, emails.get(key), key, alarmTime);
}
} catch (Exception ex) {
logger.error("<XNMSProxyService CallBackThread>", ex);
}
}
/// 发送某一种语言的中转台上下线邮件
/// <param name="cbo"></param>
/// <param name="email"></param>
/// <param name="repeater"></param>
/// <param name="emails"></param>
/// <param name="language"></param>
private void sendStateOneLanguage(ClientCallbackObject cbo, EmailHelper email, Repeater repeater,
List<String> emails, String language, java.util.Date alarmTime) throws CloneNotSupportedException {
email.setMailToArray(emails); // 转换 List 到数组
// 中转台别名
String alias = "";
// 中转台
String sn = cbo.getInfokey().toString();
// 中转台ID
String radioID = "";
// 站点ID
String siteID = "";
// 站点别名
String siteAlias = "";
if (repeater != null) {
alias = repeater.getAlias();
radioID = repeater.getRadioId().toString();
siteID = repeater.getSiteId().toString();
siteAlias = repeater.getSiteAlias();
}
if ((Utility.EnumRepeaterState) cbo.getInfovalue() == Utility.EnumRepeaterState.Offline) {
if (proxyServCastle.obtainProxyServInfo().getSystemType() == Utility.EnumSystemType.Conventional) {
// XNMS系统 中转台:{0} {1} 下线
email.setMailSubject(MessageFormat.format(getAlarmString(language, "OfflineForConventional"), alias, sn));
} else {
// XNMS系统 站点:{0} {1} 中转台:{2} {3} 下线
email.setMailSubject(MessageFormat.format(getAlarmString(language, "Offline"), siteAlias, siteID, alias, sn));
}
} else {
if (proxyServCastle.obtainProxyServInfo().getSystemType() == Utility.EnumSystemType.Conventional) {
// XNMS系统 中转台:{0} {1} 上线
email.setMailSubject(MessageFormat.format(getAlarmString(language, "OnlineForConventional"), alias, sn));
} else {
// XNMS系统 站点:{0} {1} 中转台:{2} {3} 上线
email.setMailSubject(MessageFormat.format(getAlarmString(language, "Online"), siteAlias, siteID, alias, sn));
}
}
email.setMailBody(email.getMailSubject());
if (email.sendEmail()) {
logger.debug(String.format("<XNMSProxyService CallBackThread> send email success. Info[%s]", email.getMailSubject()));
} else {
logger.info(String.format("<XNMSProxyService CallBackThread> send email fail. Info[%s]", email.getMailSubject()));
}
}
}
EmailHelper.java
package com.xnms.service.center.utility;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
@Component
public class EmailHelper {
private static Logger logger = LoggerFactory.getLogger(EmailHelper.class);
@Autowired
private JavaMailSender mailSender;
// 发送者
private String mailFrom;
//SMTP邮件服务器端口
private Integer smtpPort;
//SSL加密
private Integer enableSsl;
// 收件人
private List<String> mailToArray;
// 抄送
private String[] mailCcArray;
// 标题
private String mailSubject;
// 正文
private String mailBody;
// 发件人密码
private String mailPwd;
// SMTP邮件服务器
private String host;
// 正文是否是html格式
private boolean isBodyHtml;
// 附件
private List<Attachment> attachments = new ArrayList<>();
public String getMailFrom() {
return mailFrom;
}
public void setMailFrom(String mailFrom) {
this.mailFrom = mailFrom;
}
public List<String> getMailToArray() {
return mailToArray;
}
public void setMailToArray(List<String> mailToArray) {
this.mailToArray = mailToArray;
}
public String[] getMailCcArray() {
return mailCcArray;
}
public void setMailCcArray(String[] mailCcArray) {
this.mailCcArray = mailCcArray;
}
public String getMailSubject() {
return mailSubject;
}
public void setMailSubject(String mailSubject) {
this.mailSubject = mailSubject;
}
public String getMailBody() {
return mailBody;
}
public void setMailBody(String mailBody) {
this.mailBody = mailBody;
}
public String getMailPwd() {
return mailPwd;
}
public void setMailPwd(String mailPwd) {
this.mailPwd = mailPwd;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public Integer getSmtpPort() {
return smtpPort;
}
public void setSmtpPort(Integer smtpPort) {
this.smtpPort = smtpPort;
}
public Integer getEnableSsl() {
return enableSsl;
}
public void setEnableSsl(Integer enableSsl) {
this.enableSsl = enableSsl;
}
public boolean isBodyHtml() {
return isBodyHtml;
}
public void setBodyHtml(boolean bodyHtml) {
isBodyHtml = bodyHtml;
}
public List<Attachment> getAttachments() {
return attachments;
}
public void setAttachments(List<Attachment> attachments) {
this.attachments = attachments;
}
@Override
public String toString() {
return "EmailHelper{" +
"mailSender=" + mailSender +
", mailFrom='" + mailFrom + '\'' +
", smtpPort=" + smtpPort +
", enableSsl=" + enableSsl +
", mailToArray=" + mailToArray +
", mailCcArray=" + Arrays.toString(mailCcArray) +
", mailSubject='" + mailSubject + '\'' +
", mailBody='" + mailBody + '\'' +
", mailPwd='" + mailPwd + '\'' +
", host='" + host + '\'' +
", isBodyHtml=" + isBodyHtml +
", attachments=" + attachments +
'}';
}
//String mailFrom, List<String> mailToArray, List<String> mailCcArray,
//String mailSubject, String mailBody, boolean isBodyHtml,
//List<String> attachments, String mailPwd, String host, int smtpPort, boolean enableSsl
public boolean sendEmail() {
try {
// 初始化邮件消息
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
mailSender.setHost(host);
mailSender.setPort(smtpPort);
mailSender.setUsername(mailFrom);
mailSender.setPassword(mailPwd);
// 设置额外的SMTP属性
Properties props = mailSender.getJavaMailProperties();
props.put("mail.transport.protocol", "smtp");
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.ssl.enable", enableSsl == 1 ? "true" : "false");
props.put("mail.debug", "true");
MimeMessage mimeMessage = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, StandardCharsets.UTF_8.name());
// 设置发件人
helper.setFrom(mailFrom);
logger.info("<EmailService> mailFrom: {}", mailFrom);
// 创建一个列表来存储有效的收件人
List<String> validRecipients = new ArrayList<>();
// 添加收件人
if (mailToArray != null) {
for (int i = 0; i < mailToArray.size(); i++) {
String recipient = mailToArray.get(i).trim();
if (recipient.contains("@")) {
helper.addTo(recipient);
validRecipients.add(recipient); // 存储有效收件人
logger.info("<EmailService> mailTo{} : {} ", i, recipient);
}
}
}
// 如果没有收件人,则返回
if (validRecipients.isEmpty()) {
return true;
}
// 添加抄送
if (mailCcArray != null) {
for (String cc : mailCcArray) {
helper.addCc(cc);
}
}
// 设置邮件主题
helper.setSubject(mailSubject);
helper.setText(mailBody, isBodyHtml);
helper.setPriority(1); // 设置为高优先级
try {
// 添加附件
if (attachments != null && !attachments.isEmpty()) {
for (Attachment attachmentPath : attachments) {
File file = new File(attachmentPath.getPath());
helper.addAttachment(file.getName(), file);
}
}
} catch (MessagingException e) {
logger.error("EmailHelper类,sendEmail方法:",e);
}
// 发送邮件
mailSender.send(mimeMessage);
return true;
} catch (Exception ex) {
logger.error("<EmailService> Exception: " + ex.getMessage(), ex);
return false;
}
}
}
DecryptionManager.java 授权码加密工具
package com.xnms.data.contract.security;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
/**
* 此类包含各种解密算法接口,解密算法本身可另起新类实现,也可以在本类实现,视复杂度而定。
*/
public class DecryptionManager {
private static final Logger logger = LoggerFactory.getLogger(DecryptionManager.class);
/// 专门给登录时的用户名和密码解密,未知解密算法名称,特殊处理
public static String decryptLoginInfo(String text) {
try {
byte[] key = new byte[] {
(byte) 0xA6, (byte) 0x7D, (byte) 0xE1, (byte) 0x3F,
(byte) 0x35, (byte) 0x0E, (byte) 0xE1, (byte) 0xA9,
(byte) 0x83, (byte) 0xA5, (byte) 0x62, (byte) 0xAA,
(byte) 0x7A, (byte) 0xAE, (byte) 0x79, (byte) 0x98,
(byte) 0xA7, (byte) 0x33, (byte) 0x49, (byte) 0xFF,
(byte) 0xE6, (byte) 0xAE, (byte) 0xBF, (byte) 0x8D,
(byte) 0x8D, (byte) 0x20, (byte) 0x8A, (byte) 0x49,
(byte) 0x31, (byte) 0x3A, (byte) 0x12, (byte) 0x40
};
byte[] iv = new byte[] {
(byte) 0xF8, (byte) 0x8B, (byte) 0x01, (byte) 0xFB,
(byte) 0x08, (byte) 0x85, (byte) 0x9A, (byte) 0xA4,
(byte) 0xBE, (byte) 0x45, (byte) 0x28, (byte) 0x56,
(byte) 0x03, (byte) 0x42, (byte) 0xF6, (byte) 0x19
};
SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
byte[] encryptedData = Base64.getDecoder().decode(text);
byte[] decryptedData = cipher.doFinal(encryptedData);
return new String(decryptedData, StandardCharsets.UTF_8);
} catch (Exception e) {
logger.error("decryptLoginInfo error", e);
return "";
}
}
public static String encryptLoginInfo(String text) {
try {
byte[] key = new byte[]{
(byte) 0xA6, (byte) 0x7D, (byte) 0xE1, (byte) 0x3F,
(byte) 0x35, (byte) 0x0E, (byte) 0xE1, (byte) 0xA9,
(byte) 0x83, (byte) 0xA5, (byte) 0x62, (byte) 0xAA,
(byte) 0x7A, (byte) 0xAE, (byte) 0x79, (byte) 0x98,
(byte) 0xA7, (byte) 0x33, (byte) 0x49, (byte) 0xFF,
(byte) 0xE6, (byte) 0xAE, (byte) 0xBF, (byte) 0x8D,
(byte) 0x8D, (byte) 0x20, (byte) 0x8A, (byte) 0x49,
(byte) 0x31, (byte) 0x3A, (byte) 0x12, (byte) 0x40
};
byte[] iv = new byte[]{
(byte) 0xF8, (byte) 0x8B, (byte) 0x01, (byte) 0xFB,
(byte) 0x08, (byte) 0x85, (byte) 0x9A, (byte) 0xA4,
(byte) 0xBE, (byte) 0x45, (byte) 0x28, (byte) 0x56,
(byte) 0x03, (byte) 0x42, (byte) 0xF6, (byte) 0x19
};
SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec);
byte[] encryptedData = cipher.doFinal(text.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(encryptedData);
} catch (Exception e) {
logger.error("encryptLoginInfo error", e);
return "";
}
}
public static void main(String[] args) {
System.out.println(encryptLoginInfo("4QuMvTdwa8mCfWMP"));
}
}
application.properties 配置文件
xnms.service.center.MailFrom=
xnms.service.center.MailPwd=WwpvHsA5h9Bw4bjp2CXK2d/MVSfmKFfA9cGGPWCm9VA=
xnms.service.center.MailHost=smtp.exmail.qq.com
# 【不加密端口号25、ssl加密端口465】
xnms.service.center.SmtpPort=25
xnms.service.center.EnableSsl=0
messages_zh_CN.properties 词条
forward1=低前向功率告警--------------------------------------------------------------------------------当本机检测到前向功率低于预设值时,告警状态指示灯亮红灯,并且LCD将弹出如下告警提示:
forward2=出现此告警提示时,本机将根据检测状态选择继续发射,或者停止发射。此时您需要采取如下措施:\u3000检查与发射机连接的射频转接线或天线/馈线是否松脱或损坏。如果是,请及时紧固或更换连接线。联系经销商或专业人员检查本机的功放模块是否正常。若现场无法解决,请联系您所在地的经销商提供协助。当前向功率恢复到正常值时,告警提示框将自动消失,并且告警状态指示灯熄灭。
RXUnlock1=接收失锁告警--------------------------------------------------------------------------------当本机检测到接收锁相环失锁时,告警状态指示灯亮红灯,并且LCD将弹出如下告警提示:
RXUnlock2=接收失锁告警
RXTXUnlock3=出现此告警提示时,本机的部分功能将自动停止,但仍会保留LCD告警提示。此时您需要采取如下措施:断开电源,打开机箱,观察硬件连接线是否连接松脱或损坏。若是,请及时紧固或更换硬件连接线。若现场无法解决,请联系您所在地的经销商提供协助。 当发射/接收失锁恢复正常时,告警状态指示灯熄灭。但告警提示框不会消失,需要进行其他操作来间接清理此提示框,例如按中转台界面上的上下按钮。注意在您打开机箱检查失锁告警前,请断开电源。
Temperature1=过温告警--------------------------------------------------------------------------------当本机检测到功放模块内部温度超出正常工作温度范围时,告警状态指示灯亮红灯,并且LCD将弹出如下告警提示:
Temperature2=出现此告警提示时,本机将停止发射。此时您需要采取如下措施恢复发射:检查本机的功放模块表面温度是否过高,例如超过80℃。如果温度过高,请按照步骤2和3检查原因。注意此时本机的功放散热器可能处于高温状态,切勿用手触摸本机,您可以使用带热电耦的数字温度计进行测量。检查本机的风扇能否正常运行。若不能正常运行,请尽快更换风扇。检查环境温度和本机的通风条件。若不符合中转台说明书中要求的安装条件,请尽快改善安装环境,例如:加装空调设备,促进通风,降低温度。 检查与发射机连接的射频转接线或天线/馈线是否松脱或损坏,是否由于连接不良导致发射功率过高,使散热器温升过快。如果是,请及时紧固或更换连接线。若非上述情况,请联系您所在地的经销商协助解决。 当本机温度恢复到正常范围时,告警提示框将自动消失,并且告警状态指示灯熄灭。
voltage1=--------------------------------------------------------------------------------当本机检测到电压超出了电压范围(11V~15.6V)时,告警状态指示灯亮红灯,并且LCD将弹出如下告警提示:
voltage2_Low=低压告警
voltage3=出现此告警提示时,本机将自动停止工作,但仍会保留LCD告警提示。此时您需要采取如下措施:使用电压表检查直流电源输入的电压是否过低或过高。如果是,请及时更换直流电源或外接电池。检查直流电源线是否松脱或损坏。如果是,请及时紧固或更换直流电源线。 若现场无法解决,请联系您所在地的经销商提供协助。当电压恢复到正常工作电压范围时,告警提示框将自动消失,并且告警状态指示灯熄灭。注意若使用外接电池对本机供电并发现电压过低,请及时进行充电。充电前,请断开本机与电池的连接。
Vswr1=驻波比告警--------------------------------------------------------------------------------发射天线端口的电压驻波比(VSWR)过大会损坏射频功放,严重情况下会导致发射机无法正常工作。当本机检测到电压驻波比超出正常值时,告警状态指示灯亮红灯,并且LCD将弹出如下告警提示:
Vswr2=出现此告警提示时,本机将自动降低发射功率。此时您需要采取如下措施:\u3000检查发射频点是否处于天线的工作频率范围内。选择错误的天线会导致发射机性能下降,并可能会损坏发射机。如果否,请联系您所在地的经销商更换天线。检查与发射机连接的射频转接线或天线/馈线是否松脱或损坏。如果是,请及时更换连接线。 若现场无法解决,请联系您所在地的经销商提供协助。当电压驻波比恢复到正常值时,告警提示框将自动消失,并且告警状态指示灯熄灭。
EmailConclusion=中转台 别名:{0} SN:{1} 出现{2},{3}指标出现故障请尽快处理
EmailTitle=XNMS系统 站点(别名:{0} ID:{1}) 中转台(别名:{2} SN:{3}) 出现{4}!请尽快处理
Offline=XNMS系统 站点(别名:{0} ID:{1}) 中转台(别名:{2} SN:{3}) 下线
Online=XNMS系统 站点(别名:{0} ID:{1}) 中转台(别名:{2} SN:{3}) 上线
UnknownType=未知
UnknownAlarm=未知告警
voltage2_High=过压告警
TXUnlock1=发射失锁告警--------------------------------------------------------------------------------当本机检测到发射锁相环失锁时,告警状态指示灯亮红灯,并且LCD将弹出如下告警提示:
TXUnlock2=
EmailTitleForConventional=XNMS系统 中转台(别名:{0} SN:{1}) 出现{2}!请尽快处理
OfflineForConventional=XNMS系统 中转台(别名:{0} SN:{1}) 下线
OnlineForConventional=XNMS系统 中转台(别名:{0} SN:{1}) 上线
照片静态资源存放路径
这些都是邮件附件中要使用的图片资源,初始放在resources/Image路径下。