基于springboot +webservice+XxlJob发送邮件

一、需求分析

     在分布式环境下,需要使用webservice发送邮件,然后实现每隔一段时间定时发送待发邮件的功能。

二、基本的配置信息

1. 需要配置邮件服务器的相关信息, 配置WebService地址等。

# 邮件基础数据配置
fec-peripheral-service.webserviceaddress=
fec-peripheral-service.mailSysId=
fec-peripheral-service.mailUid=
fec-peripheral-service.mailServiceName=
#fec-peripheral-service.mailFrom=
fec-peripheral-service.mailFrom=
fec-peripheral-service.mailNoName=

需要的依赖:

<!--webservice 调用需要的jar -->
		<dependency>
			<groupId>commons-logging</groupId>
			<artifactId>commons-logging</artifactId>
			<version>1.0.4</version>
		</dependency>

		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.11</version>
		</dependency>

		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.8</version>
		</dependency>

		<dependency>
			<groupId>org.apache.axis</groupId>
			<artifactId>axis</artifactId>
			<version>1.4</version>
		</dependency>

		<dependency>
			<groupId>org.apache.axis</groupId>
			<artifactId>axis-jaxrpc</artifactId>
			<version>1.4</version>
		</dependency>

		<dependency>
			<groupId>commons-discovery</groupId>
			<artifactId>commons-discovery</artifactId>
			<version>0.2</version>
		</dependency>

		<dependency>
			<groupId>org.apache.axis</groupId>
			<artifactId>axis-saaj</artifactId>
			<version>1.4</version>
		</dependency>

		<dependency>
			<groupId>wsdl4j</groupId>
			<artifactId>wsdl4j</artifactId>
			<version>1.4</version>
		</dependency>
		<!--webservice 调用需要的jar -->

2. 实现发邮件

      将邮件的内容以XML的形式来发送一个HTTP请求:

 @Override
    public processresult sendMail(sysInstMailLog sysInstMailLog) {
        org.apache.axis.client.Service service = new org.apache.axis.client.Service();
        Call call = null;
        try {
            call = (Call) service.createCall();
            call.setTargetEndpointAddress(""+sysInstMailLog.getWebServiceAddress()+"");
            call.setOperationName(QName.valueOf("execute"));
        } catch (ServiceException e1) {
            e1.printStackTrace();
        }
        //发送的XML不能包括多余的空格,否则WSP平台会系统错误 参数值不能有null,否则邮件发送会失败
        String request =
                "<?xml version='1.0' encoding='GBK'?>" +
                        "<request version='1.0'>" +
                        "<system>" +
                        "<sysid>"+sysInstMailLog.getMailSysId()+"</sysid>" +
                        "<uid>"+sysInstMailLog.getMailUid()+"</uid>" +
                        "<servicename>"+sysInstMailLog.getMailServiceName()+"</servicename>" +
                        "</system>" +
                        "<query>" +
                        "<filed name='MAILNO' type='String'>" + sysInstMailLog.getMailNo() + "</filed>" +
                        "<filed name='SUBJECT' type='String'>" + sysInstMailLog.getMailSubject() + "</filed>" +
                        "<filed name='CONTENT' type='String'>" + sysInstMailLog.getMailContent() + "</filed>" +
                        "<filed name='FROM' type='String'>" + sysInstMailLog.getMailFrom() + "</filed>" +
                        //<!--邮件接收人,多个邮件接收人以分号分隔 -->
                        "<filed name='TO' type='String'>" + sysInstMailLog.getMailTo() + "</filed>" +
                        "<filed name='CC' type='String'>" + sysInstMailLog.getMailCc() + "</filed>" +
                        "<filed name='BCC' type='String'>" + sysInstMailLog.getMailBcc() + "</filed>" +
                        //文件路径需要包括文件名
                        "<filed name='FILEPATH' type='String'>" + sysInstMailLog.getMailFilePath() + "</filed>" +
                        "<filed name='FILENAME' type='String'>" + sysInstMailLog.getMailFileName() + "</filed>" +
                        "<filed name='MEMO' type='String'>" + sysInstMailLog.getMailMemo() + "</filed>" +
                        "<filed name='IMMEDIATE' type='String'>" + sysInstMailLog.getMailImmediate() + "</filed>" +
                        "<filed name=\"POLICYNUMBER\" type=\"String\">" + sysInstMailLog.getPolicyNumber() + "</filed>" +
                        "</query>" +
                        "</request>";
        try {
            String response = (String) call.invoke(new Object[]{request});
            if (null != response) {
                System.out.println("WebService Response:"+response);
                processResult=ParaseXml.GetXmlResultCodeNodeText(response,sysInstMailLog);
            }
        } catch (Exception e) {
            //写log
            e.printStackTrace();
        }

        return processResult;
    }

三、核心实现

       将要发送邮件的内容以及信息以根据平台提供的一个xml文件的形式包起来,然后以object[]数组的形式来调用,然后通过call调用即可将内容以xml报文的发送给webservice平台, 具体的方式为是通过 axis的客户端的service调用createCall()方法创建一个call,然后使用设置设置targetEndpointAddress()属性值,最后将xml报文的内容放在call.invoke(new Object[]{报文内容}):

import javax.xml.rpc.Call;
org.apache.axis.client.Service service = new org.apache.axis.client.Service();
Call call = null;
try {
    call = (Call) service.createCall();
    call.setTargetEndpointAddress(""+sysInstMailLog.getWebServiceAddress()+"");
    call.setOperationName(QName.valueOf("execute"));


String response = (String) call.invoke(new Object[]{request});

远程调用webservice服务器发送邮件消息的完整代码:

package com.hand.hcf.app.peripheral.mail.service.Impl;


import com.hand.hcf.app.mdata.client.workflow.WorkflowInterface;
import com.hand.hcf.app.mdata.client.workflow.dto.ApprovalErrorDataCO;
import com.hand.hcf.app.peripheral.mail.domain.sysInstMailLog;
import com.hand.hcf.app.peripheral.mail.dto.processresult;
import com.hand.hcf.app.peripheral.mail.persistence.SysInstMailLogMapper;
import com.hand.hcf.app.peripheral.mail.service.MailService;
import com.hand.hcf.app.peripheral.mail.utils.ParaseXml;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.xml.namespace.QName;
import javax.xml.rpc.Call;
import javax.xml.rpc.ServiceException;


@Service
public class MailServiceImpl implements MailService {

    /*自动装配 数据源事务管理
    * 前提是容器中有可装配的事务管理器 如果没有需要注入
    * */
    private final DataSourceTransactionManager transactionManager;
    private processresult processResult;

    @Autowired
    private WorkflowInterface workflowInterface;

    public MailServiceImpl(DataSourceTransactionManager transactionManager) {
        this.transactionManager=transactionManager;
    }


    @Autowired
    private SysInstMailLogMapper sysInstMailLogMapper;
    ParaseXml ParaseXml=new ParaseXml();

    @Override
    public int addSysInstMailLogRecord(sysInstMailLog sysInstMailLog) {
        StringBuffer sb=new StringBuffer();
        ApprovalErrorDataCO c0=new ApprovalErrorDataCO();
        c0.setApplicationName("fec-peripheral");
        sb.append("系统Id:"+sysInstMailLog.getMailSysId());
      try{
          sysInstMailLogMapper.add_sysInstMailLog_record(sysInstMailLog);
      }catch (Exception e){
          sb.append("错误:"+e.toString()+"报账单号为:"+sysInstMailLog.getMail_reportnumber());
      }
      String str=sb.toString();
        c0.setErrorMsg(str);
      if (str.contains("错误")){
          workflowInterface.createApprovalErrorData(c0);
      }
        return sysInstMailLog.getId();
    }

    @Override
    public processresult sendMail(sysInstMailLog sysInstMailLog) {
        org.apache.axis.client.Service service = new org.apache.axis.client.Service();
        Call call = null;
        try {
            call = (Call) service.createCall();
            call.setTargetEndpointAddress(""+sysInstMailLog.getWebServiceAddress()+"");
            call.setOperationName(QName.valueOf("execute"));
        } catch (ServiceException e1) {
            e1.printStackTrace();
        }
        //发送的XML不能包括多余的空格,否则WSP平台会系统错误 参数值不能有null,否则邮件发送会失败
        String request =
                "<?xml version='1.0' encoding='GBK'?>" +
                        "<request version='1.0'>" +
                        "<system>" +
                        "<sysid>"+sysInstMailLog.getMailSysId()+"</sysid>" +
                        "<uid>"+sysInstMailLog.getMailUid()+"</uid>" +
                        "<servicename>"+sysInstMailLog.getMailServiceName()+"</servicename>" +
                        "</system>" +
                        "<query>" +
                        "<filed name='MAILNO' type='String'>" + sysInstMailLog.getMailNo() + "</filed>" +
                        "<filed name='SUBJECT' type='String'>" + sysInstMailLog.getMailSubject() + "</filed>" +
                        "<filed name='CONTENT' type='String'>" + sysInstMailLog.getMailContent() + "</filed>" +
                        "<filed name='FROM' type='String'>" + sysInstMailLog.getMailFrom() + "</filed>" +
                        //<!--邮件接收人,多个邮件接收人以分号分隔 -->
                        "<filed name='TO' type='String'>" + sysInstMailLog.getMailTo() + "</filed>" +
                        "<filed name='CC' type='String'>" + sysInstMailLog.getMailCc() + "</filed>" +
                        "<filed name='BCC' type='String'>" + sysInstMailLog.getMailBcc() + "</filed>" +
                        //文件路径需要包括文件名
                        "<filed name='FILEPATH' type='String'>" + sysInstMailLog.getMailFilePath() + "</filed>" +
                        "<filed name='FILENAME' type='String'>" + sysInstMailLog.getMailFileName() + "</filed>" +
                        "<filed name='MEMO' type='String'>" + sysInstMailLog.getMailMemo() + "</filed>" +
                        "<filed name='IMMEDIATE' type='String'>" + sysInstMailLog.getMailImmediate() + "</filed>" +
                        "<filed name=\"POLICYNUMBER\" type=\"String\">" + sysInstMailLog.getPolicyNumber() + "</filed>" +
                        "</query>" +
                        "</request>";
        try {
            String response = (String) call.invoke(new Object[]{request});
            if (null != response) {
                System.out.println("WebService Response:"+response);
                processResult=ParaseXml.GetXmlResultCodeNodeText(response,sysInstMailLog);
            }
        } catch (Exception e) {
            //写log
            e.printStackTrace();
        }

        return processResult;
    }

    @Override
    public void HandlingMailDeliveryResults(processresult processresult) {
        /*配置事务策略*/
        DefaultTransactionDefinition def=new DefaultTransactionDefinition();
        def.setName("planeOne-transation");
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        /*设置状态点*/
        TransactionStatus transactionStatus=transactionManager.getTransaction(def);
        String Mail_RESULTCODE=processresult.getMailResultCode();

        try {
            //do something
            /*
             * SN01 邮件发送成功
             * FN01 邮件发送失败
             * B01 收件人被拉黑名单, 本次不作发送处理
             * E01 邮件发送服务异常
             * OTHER 未知错误
             * */
            sysInstMailLogMapper.updateSysInstMaillogByID(processresult);
            //手动提交
            transactionManager.commit(transactionStatus);
        }catch (Exception ex){
            //手动回滚 写log
            transactionManager.rollback(transactionStatus);
        }

    }
}

邮件记录表(sys_init_log)的结构:

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for sys_inst_maillog
-- ----------------------------
DROP TABLE IF EXISTS `sys_inst_maillog`;
CREATE TABLE `sys_inst_maillog`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `mail_no` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '邮件编号',
  `mail_sysid` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '应用系统ID',
  `mail_uid` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '业务系统ID',
  `mail_servicename` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '服务名',
  `mail_subject` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '邮件主题',
  `mail_content` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL COMMENT '邮件正文',
  `mail_from` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '发件人',
  `mail_to` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '收件人',
  `mail_cc` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '抄送地址',
  `mail_bcc` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '密送地址',
  `mail_filepath` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '附件路径',
  `mail_filename` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '附件名称',
  `mail_memo` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '备注信息',
  `mail_tm_create` datetime(0) NULL DEFAULT NULL COMMENT '邮件创建时间',
  `mail_immediate` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL DEFAULT '1' COMMENT '是否立即发送(1立即发送)',
  `mail_status` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '处理标识 1正常|2异常|0系统异常',
  `mail_errormassage` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '错误信息正常(无)异常(有)',
  `mail_resultcode` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '处理结果SN01 邮件发送成功|FN01 邮件发送失败|B01 收件人被拉黑名单, 本次不作发送处理|E01 邮件发送服务异常|OTHER 未知错误',
  `mail_resultdesc` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '处理结果 DESC',
  `mailservice_tm_return` datetime(0) NULL DEFAULT NULL COMMENT '邮件服务返回结果时间',
  `mail_reportnumber` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '报账单号',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE INDEX `mail_reportnumber`(`mail_reportnumber`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1671 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '新费控系统邮件日志表' ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

然后调用发送接口,成功后会将结果回写到表里,成功或者失败回写到表里的mail_resultdesc字段里。

使用XxlJob设置时间段,每隔一段时间遍历该表sys_init_log。每次只查出mail_resultdesc为null的记录:

 select id,mail_no,mail_sysid,mail_uid,mail_servicename,mail_subject,mail_content,mail_from,mail_to,
mail_cc,mail_bcc,mail_filepath,mail_filename,mail_memo,mail_immediate,mail_tm_create from sys_inst_maillog where mail_resultdesc is null

Job定时任务:

package com.hand.hcf.app.peripheral.job;

import com.hand.hcf.app.peripheral.mail.domain.sysInstMailLog;
import com.hand.hcf.app.peripheral.mail.service.EmailLogQueryService;
import com.hand.hcf.app.peripheral.mail.service.EmailSendService;
import com.hand.hcf.app.peripheral.mail.service.Impl.MailServiceImpl;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.IJobHandler;
import com.xxl.job.core.handler.annotation.JobHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;


@Component
@JobHandler("sendEmail")
public class FecSendEmail extends IJobHandler {
    @Autowired
    private EmailLogQueryService emailLogQueryService;
    @Autowired
    private EmailSendService emailSendService;
    /**
     * glcs 定时任务
     * @param strings 参数
     * @return  结果
     */
    @Override
    @Transactional
    public ReturnT<String> execute(String... strings) throws Exception {
        List<sysInstMailLog> sysInstMailLogList=emailLogQueryService.queryEmailLog();
        for(sysInstMailLog sysInstMailLog:sysInstMailLogList){
            emailSendService.sendEmail(sysInstMailLog);
        }
        return ReturnT.SUCCESS;
    }
}

执行的sendEmail方法就是贴出来的第一个代码。

由于我们的job的admin单独打包成了一个服务,每个服务需要调用时需要额外添加配置:

job配置类:

package com.hand.hcf.app.mdata.job;

import com.xxl.job.core.executor.XxlJobExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class XxlJobConfig {
    private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);

    @Value("${xxl.job.admin.addresses}")
    private String adminAddresses;

    @Value("${xxl.job.executor.appname}")
    private String appName;

    @Value("${xxl.job.executor.ip}")
    private String ip;

    @Value("${xxl.job.executor.port}")
    private int port;

    @Value("${xxl.job.accessToken}")
    private String accessToken;

    @Value("${xxl.job.executor.logpath}")
    private String logPath;

    @Value("${xxl.job.executor.logretentiondays}")
    private int logRetentionDays;


    @Bean(initMethod = "start", destroyMethod = "destroy")
    public XxlJobExecutor xxlJobExecutor() {
        logger.info(">>>>>>>>>>> xxl-job config init.");
        XxlJobExecutor xxlJobExecutor = new XxlJobExecutor();
        xxlJobExecutor.setAdminAddresses(adminAddresses);
        xxlJobExecutor.setAppName(appName);
        xxlJobExecutor.setIp(ip);
        xxlJobExecutor.setPort(port);
        xxlJobExecutor.setAccessToken(accessToken);
        xxlJobExecutor.setLogPath(logPath);
        return xxlJobExecutor;
    }

}

xxl.job.admin.addresses = http://127.0.0.1:8081/xxl-job
#xxl.executor.logpath = /home/job/logs
xxl.job.executor.appname = mdata
xxl.job.executor.ip=
xxl.job.executor.port=9991
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler/
xxl.job.accessToken=
xxl.job.executor.logretentiondays= -1

主要是Job下的admin服务,在Xxl官网可以拿到全部的开放源码:

四、自己动手搭建XxlJob

我们也可以自己从Xxl的官网下载源码,自己搭建一个分布式的定时任务平台,搭出来看到的效果就是这样:

官网源码地址:  GitHub - xuxueli/xxl-job: A distributed task scheduling framework.(分布式任务调度平台XXL-JOB)

按照给的文档,搭建完毕后,直接访问如下地址即可出现主页,http://localhost:8080/xxl-job-admin/ ,  默认用户名为: admin, 密码为:123456。

在任务管理里,可以定义自己的定时器,配置好参数后,即可一键执行。

Spring Boot是一个用于创建独立的、基于生产级别的Spring应用程序的框架。它简化了Spring应用程序的配置和部署过程,并提供了一系列开箱即用的功能,包括自动配置、嵌入式服务器和监控等。 Web服务(Web Service)是一种通过网络进行通信的软件系统,它使用标准化的XML消息格式进行数据交换。Web服务可以跨编程语言和操作系统平台进行远程调用,使得不同系统之间的集成更加方便。 在Spring Boot中集成Web服务(WebService),可以使用两种方式进行调用:服务端方式和Java客户端方式。 1. 服务端方式: - 首先,在Spring Boot项目中引入相关依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web-services</artifactId> </dependency> ``` - 然后,创建一个WebService服务端类,使用`@Endpoint`注解标记该类为WebService端点,并使用`@PayloadRoot`注解指定请求的命名空间和请求名称。 - 在WebService服务端类中,定义相应的方法,使用`@ResponsePayload`注解标记方法的返回值作为响应数据。 - 最后,启动Spring Boot应用程序,WebService服务端就可以接收和处理来自客户端的请求。 2. Java客户端方式: - 首先,在Spring Boot项目中引入相关依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web-services</artifactId> </dependency> ``` - 然后,创建一个Java客户端类,使用`WebServiceTemplate`类进行远程调用。 - 在Java客户端类中,使用`WebServiceTemplate`的`marshalSendAndReceive`方法发送请求,并指定请求的URL、请求对象和响应对象的类型。 - 最后,启动Spring Boot应用程序,Java客户端就可以调用WebService服务端提供的接口。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乌托邦钢铁侠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值