当前位置: 首页 > news >正文

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.com
  • SMTP邮件服务器端口:25/465
  • SSL加密:是/否

如果“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加密端口465xnms.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℃。如果温度过高,请按照步骤23检查原因。注意此时本机的功放散热器可能处于高温状态,切勿用手触摸本机,您可以使用带热电耦的数字温度计进行测量。检查本机的风扇能否正常运行。若不能正常运行,请尽快更换风扇。检查环境温度和本机的通风条件。若不符合中转台说明书中要求的安装条件,请尽快改善安装环境,例如:加装空调设备,促进通风,降低温度。 检查与发射机连接的射频转接线或天线/馈线是否松脱或损坏,是否由于连接不良导致发射功率过高,使散热器温升过快。如果是,请及时紧固或更换连接线。若非上述情况,请联系您所在地的经销商协助解决。 当本机温度恢复到正常范围时,告警提示框将自动消失,并且告警状态指示灯熄灭。
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路径下。

相关文章:

  • 吾爱置顶软件,吊打电脑自带功能!
  • 探索高通骁龙光线追踪技术
  • 视频设备轨迹回放平台EasyCVR打造视频智能融合新平台,驱动智慧机场迈向数字新时代
  • 【HTB】Windwos-easy-Legacy靶机渗透
  • 从零开始学习PX4源码19(飞行模式管理学习)
  • PyTorch嵌入层(nn.Embedding)
  • C++从入门到实战(十)类和对象(最终部分)static成员,内部类,匿名对象与对象拷贝时的编译器优化详解
  • LeetCode 891 -- 贡献度思想
  • 【爬虫】网易云音乐评论数据爬取
  • nodejs、socket.io、express + 实时线上聊天系统(自用笔记)
  • 若依——基于AI+若依框架的实战项目(实战篇(下))
  • 大模型中的参数规模与显卡匹配
  • forms实现俄罗斯方块
  • 什么是数据仓库
  • 从效率瓶颈到智能飞跃:AI工具如何重塑 Java开发模式
  • 敏捷开发:以人为本的高效开发模式
  • Dify工作流中如何去除deepseek-r1思考内容
  • html+css+js 实现一个贪吃蛇小游戏
  • TypeScript String:深入理解与使用
  • uniapp开发app 上传视频切片上传