文章目录
- 一、项目背景
- 二、页面配置
- 三、注意点总结
- 四、邮箱收件效果
- 五、代码
- 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");@Autowiredprivate DataServCallInvoker dataServCallInvoker;@Autowiredprivate SecurityControl securityControl;@Autowiredprivate ProxyServCastle proxyServCastle;@Autowiredprivate I18NService i18NService;@Autowiredprivate 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");}@Overridepublic 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();// 中转台IDString radioID = "";// 站点IDString 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 possibleemail.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 possibleemail.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 possibleemail.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");// 从路径读取 BufferedImageBufferedImage 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();// 中转台IDString radioID = "";// 站点IDString 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);@Autowiredprivate 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;}@Overridepublic 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 enableSslpublic 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路径下。