第一次自我编程解决生活中实际遇到的问题——满满的成就感

前段时间参加了一场考试,考试的成绩分数下来,当时有过想法去写的代码监听考试成绩的公布,以及调用下微信公众号通知服务,通知自己的成绩公布时间,然后懒得写,现在成绩公布通过,填写证书的邮寄地址后,需要漫长的时间等待官方证书制作,以及证书邮寄状态,以及官方提供我的证书邮寄单号信息(因为我的证书地址填写的是老家的地址),我不可能每天登录查看自己的证书以及邮寄状态,因此编写个程序去监听自己的证件邮寄状态,然后微信公众号和邮箱双重提醒

源码已上传git:https://gitee.com/gangye/java_wechat_email_demo

一、准备微信公众号以及微信通知的服务

微信测试公众号向关注的ID发送信息步骤:

1.申请一个测试的微信公众平台的测试账号,注意此账号只是测试,生产还得用实际的。

测试申请地址:

https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login

扫码登录会获得一个appID和appsecret

 

2.然后使用接受信息的微信扫码关注测试公众号

使用自己的微信扫一扫后用户列表有了一条用户记录

3.接下来使用测试的微信公众平台给关注者发送信息通过appID和appsecret获取接口访问令牌access_token,接口调用地址:

https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=第一步中微信给的appId&secret=第一步中的appsecret

有一串返回报文

保存access_token,将作为调用微信接口的凭证。(access_token动态生成且有效期为两个小时在返回的expire_in字段说明7200表示两小时对应的时间秒单位,因此在每次调用接口前都应先获取最新的access_token,防止过期)

2.发送信息

https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=上述获取的access_token

在请求体中使用application/json的报文结合post方式

注意:具体参考微信开发说明文档,一切以官方文档为准:

https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Batch_Sends_and_Originality_Checks.html

报文案例如下:

{
    "touser": "第二步骤关注公众号的用户列表的微信号",
    "msgtype": "text",
    "text": {
        "content": "测试发送的内容文本"
    }
}

返回报文成功,然后打开微信查看是否接收信息

现在所有的工具准备完毕,接口调通成功,接下来构建项目开发代码

二.搭建项目

由于只是简单的java项目,但是便于引入外部jar包和jar包的版本管理,还是使用了maven来创建

1.编写pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.csrcb</groupId>
    <artifactId>java_wechat_demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.10.2</version>
        </dependency>

        <!--引入邮件-->
        <dependency>
            <groupId>javax.mail</groupId>
            <artifactId>mail</artifactId>
            <version>1.4.7</version>
        </dependency>

        <!--
            引入日志,控制台打印,便于测试,以及日志存储到文件中
            此处我没有使用到spring框架,直接映入了logback日志,
            使用spring框架映入logback框架的时候注意映入jar包,可以参考https://www.jianshu.com/p/696444e1a352这篇文章
            也可以看看我的这篇文章https://blog.csdn.net/xibei19921101/article/details/107912006
        -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.12.4</version>
                <configuration>
                    <forkMode>once</forkMode>
                    <argLine>-Dfile.encoding=UTF-8</argLine>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <transformers>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>com.csrcb.AppStart</mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

            <!--使用此方法打包是不是使用mvn package指令,而是mvn assembly:assembly指令-->
            <!--<plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>2.5.5</version>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>com.csrcb.AppStart</mainClass>
                        </manifest>
                    </archive>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
            </plugin>-->
        </plugins>
    </build>
</project>

2.编写日志配置文件logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--scan:
            当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
scanPeriod:
            设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。
debug:
            当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。

configuration 子节点为 appender、logger、root

            -->
<configuration scan="true" scanPeriod="60 seconds" debug="false">
    <!--用于区分不同应用程序的记录-->
    <contextName>logback</contextName>
    <!--日志文件所在目录,如果是tomcat,如下写法日志文件会在则为${TOMCAT_HOME}/bin/logs/目录下-->
    <property name="LOG_HOME" value="logs"/>

    <!--控制台-->
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度 %logger输出日志的logger名 %msg:日志消息,%n是换行符 -->
            <pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%thread] %-5level %logger{36} : %msg%n</pattern>
            <!--解决乱码问题-->
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <!--滚动文件-->
    <appender name="infoFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- ThresholdFilter:临界值过滤器,过滤掉 TRACE 和 DEBUG 级别的日志 -->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/log.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory><!--保存最近30天的日志-->
        </rollingPolicy>
        <encoder>
            <charset>UTF-8</charset>
            <pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%thread] %-5level %logger{36} : %msg%n</pattern>
        </encoder>
    </appender>

    <!--滚动文件-->
    <appender name="errorFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- ThresholdFilter:临界值过滤器,过滤掉 TRACE 和 DEBUG 级别的日志 -->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>error</level>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory><!--保存最近30天的日志-->
        </rollingPolicy>
        <encoder>
            <charset>UTF-8</charset>
            <pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%thread] %-5level %logger{36} : %msg%n</pattern>
        </encoder>
    </appender>

    <!--将日志输出到logstack-->
    <!--<appender name="logstash" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
        <destination>47.93.173.81:7002</destination>
        <encoder class="net.logstash.logback.encoder.LogstashEncoder">
            <charset>UTF-8</charset>
        </encoder>
        <keepAliveDuration>5 minutes</keepAliveDuration>
    </appender>-->

    <!--这里如果是info,spring、mybatis等框架则不会输出:TRACE < DEBUG < INFO <  WARN < ERROR-->
    <!--root是所有logger的祖先,均继承root,如果某一个自定义的logger没有指定level,就会寻找
    父logger看有没有指定级别,直到找到root。-->
    <root level="debug">
        <appender-ref ref="stdout"/>
        <appender-ref ref="infoFile"/>
        <appender-ref ref="errorFile"/>
        <!--<appender-ref ref="logstash"/>-->
    </root>

    <!--为某个包单独配置logger

    比如定时任务,写代码的包名为:com.seentao.task
    步骤如下:
    1、定义一个appender,取名为task(随意,只要下面logger引用就行了)
    appender的配置按照需要即可


    2、定义一个logger:
    <logger name="com.seentao.task" level="DEBUG" additivity="false">
      <appender-ref ref="task" />
    </logger>
    注意:additivity必须设置为false,这样只会交给task这个appender,否则其他appender也会打印com.csrcb.task里的log信息。

    3、这样,在com.seentao.task的logger就会是上面定义的logger了。
    private static Logger logger = LoggerFactory.getLogger(Class1.class);
    -->

</configuration>

3.编写get请求与post请求的工具类,也可使用hutool的工具类

package com.csrcb.tools;

import com.csrcb.constant.EmsInfoUrlValue;
import com.csrcb.constant.ParamConstant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;

/**
 * @Classname HttpSendContent
 * @Description 原生的http的get和post请求,全程使用jdk自带的api,日志除外
 * @Date 2020/12/16 9:17
 * @Created by gangye
 */
public class HttpSendContent {
    private static Logger logger = LoggerFactory.getLogger(HttpSendContent.class);

    /**
     * 使用post发送请求
     *
     * @param requestUrl     请求的路由
     * @param requestBodyStr 请求体报文
     * @return
     */
    public static String doPost(String requestUrl, String requestBodyStr, String contentType, String acceptType, Map<String, String> headInfo) {
        OutputStreamWriter osw = null;
        BufferedReader br = null;
        StringBuilder result = new StringBuilder();
        HttpURLConnection conn = null;
        try {
            URL url = new URL(requestUrl);
            //通过远程url连接对象打开一个连接,强转成HTTPURLConnection类
            conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod(ParamConstant.POST_REQUEST_METHOD);
            //发送post请求必须设置为true
            conn.setDoOutput(true);
            conn.setDoInput(true);
            //设置连接超时事件和去读超时时间
            conn.setConnectTimeout(30000);
            conn.setReadTimeout(10000);
            conn.setRequestProperty("Content-Type", contentType);
            conn.setRequestProperty("Accept", acceptType);
            //增加请求头信息
            if (headInfo != null && !headInfo.isEmpty()){
                for(String key : headInfo.keySet()){
                    conn.setRequestProperty(key, headInfo.get(key));
                }
            }
            //获取输出流
            osw = new OutputStreamWriter(conn.getOutputStream());
            osw.write(requestBodyStr);
            osw.flush();
            osw.close();
            //取得输入流
            if (200 == conn.getResponseCode()) {
                br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
                String line;
                while ((line = br.readLine()) != null) {
                    result.append(line);
                    logger.info("doPost方法的请求返回的报文:{}", result);
                }
            } else {
                logger.error("ResponseCode is an error code:{}", conn.getResponseCode());
            }
        } catch (MalformedURLException e) {
            logger.error("请求的url:{}异常", requestUrl);
        } catch (IOException ie) {
            logger.error("流处理异常异常");
        } finally {
            try {
                StreamHandler.handleCloseStream(osw, br);
            } catch (IOException e) {
                logger.info("最终流关闭失败!");
            }
        }
        return result.toString();
    }

    public static String doGet(String requestUrl, String acceptType, Map<String, String> headInfo) {
        InputStream is = null;
        BufferedReader br = null;
        StringBuilder result = new StringBuilder();
        HttpURLConnection conn = null;
        try {
            URL url = new URL(requestUrl);
            //通过远程url连接对象打开一个连接,强转成HTTPURLConnection类
            conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod(ParamConstant.GET_REQUEST_METHOD);
            //设置连接超时事件和去读超时时间
            conn.setConnectTimeout(30000);
            conn.setReadTimeout(10000);
            conn.setRequestProperty("Accept", acceptType);
            conn.setRequestProperty("APIKey", EmsInfoUrlValue.getQueryapikey());
            conn.setRequestProperty("CCBP01Site.Passport.Token",EmsInfoUrlValue.getQueryemstoken());
            //增加请求头信息
            if (headInfo!=null && !headInfo.isEmpty()){
                for(String key : headInfo.keySet()){
                    conn.setRequestProperty(key, headInfo.get(key));
                }
            }
            //发送请求
            conn.connect();
            //取得输入流
            if (200 == conn.getResponseCode()) {
                is = conn.getInputStream();
                br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
                String line;
                while ((line = br.readLine()) != null) {
                    result.append(line);
                    logger.info("doGet方法的请求返回的报文:{}", result);
                }
            } else {
                logger.error("ResponseCode is an error code:{}", conn.getResponseCode());
            }
        } catch (MalformedURLException e) {
            logger.error("请求的url:{}异常", requestUrl);
        } catch (IOException ie) {
            logger.error("流处理异常异常");
        } finally {
            try {
                StreamHandler.handleCloseStream(is, br);
            } catch (IOException e) {
                logger.info("最终流关闭失败!");
            }
            conn.disconnect();
        }
        return result.toString();
    }
    //还可以会用apache的http请求jar工具实现get与post请求
    //参考https://www.jianshu.com/p/117264481886
}

若要映入hutool的jar包工具只需在pom文件中引入,(若节省开发周期,强烈推荐hutuul的工具jar包,若想学习可以看看别人的源码,自己动手还是手敲,多敲敲键盘,不是有那句话:你不秀键盘一脸,键盘就秀你一脸!!!

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>4.5.17</version>
</dependency>

4.编写配置文件,并编写代码读取配置文件的属性和属性值赋予静态变量

可以参考我的这票文章https://mp.csdn.net/console/editor/html/107342434

5.编写使用微信公众号发送信息的java服务文件,编写邮件发送的工具类

public class WeChatService {
    private static Logger logger = LoggerFactory.getLogger(WeChatService.class);

    /**
     * 通过appID和appsecret获取接口访问令牌access_token
     * @author qigangye
     * @date 2020年12月16日14:49:16
     */
    public String getAccessToken (){
        String accessToken = null;
        StringBuilder requestUrl = new StringBuilder();
        requestUrl.append(ParamConstant.GET_ACCESS_TOKEN_URL).append(ParamConstant.AND_SYMBOL)
                .append(ParamConstant.APP_ID).append(ParamConstant.EQUAL_SYMBOL)
                .append(FileParamValue.getAppid()).append(ParamConstant.AND_SYMBOL)
                .append(ParamConstant.APP_SECRET).append(ParamConstant.EQUAL_SYMBOL)
                .append(FileParamValue.getSecret());
        logger.info("获取accesstoken的get请求的路由:{}",requestUrl.toString());
        String response = HttpSendContent.doGet(requestUrl.toString(),ParamConstant.ACCTYPE_TYPE_JSON, null);
        logger.info("返回报文:{}",response);
        //将返回的json报文与定义的类的属性相映射
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            WeChatAccessTokenTestEnvironment wechatToken = objectMapper.readValue(response, WeChatAccessTokenTestEnvironment.class);
            accessToken = wechatToken.getAccessToken();
            logger.info("获得的口令:{}",accessToken);
        } catch (JsonProcessingException e) {
            logger.error("json格式转化异常:{}",response);
        }
        return accessToken;
    }

    /**
     * 调用微信发送信息,根据返回状态码值判断是否成功
     * @param message
     * @return
     */
    public boolean sendMessageWithToken (String message){
        StringBuilder requestUrl = new StringBuilder();
        requestUrl.append(ParamConstant.SEND_MESSAGE_URL).append(ParamConstant.QUESTION_SYMBOL)
                .append(ParamConstant.ACCESS_TOKEN).append(ParamConstant.EQUAL_SYMBOL)
                .append(this.getAccessToken());
        Text text = new Text();
        if (message == null || "".equals(message)){
            text.setContent("时间:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+"\n"+"地点:苏州");
        }else{
            text.setContent(message);
        }
        WeChatSendMessage weChatSendMessage = new WeChatSendMessage();
        weChatSendMessage.setTouser(FileParamValue.getTouser());
        weChatSendMessage.setMsgtype(ParamConstant.MSGTYPE_TEXT);
        weChatSendMessage.setText(text);
        String requestJson = null;
        try {
            requestJson = new ObjectMapper().writeValueAsString(weChatSendMessage);
            logger.info("发送消息请求路由:{}",requestUrl.toString());
            logger.info("发送消息请求json报文:{}",requestJson);
        } catch (JsonProcessingException e) {
            logger.error("将实体类映射为json字符串失败:{}",weChatSendMessage.toString());
        }
        String sendResponse = HttpSendContent.doPost(requestUrl.toString(),requestJson,ParamConstant.CONTENT_TYPE_JSON,ParamConstant.ACCTYPE_TYPE_JSON,null);
        logger.info("发送后返回的报文:{}",sendResponse);
        WechatSendResponse ws = new WechatSendResponse();
        try {
            ws = new ObjectMapper().readValue(sendResponse,WechatSendResponse.class);
        } catch (JsonProcessingException e) {
            logger.error(e.getMessage());
        }
        if (ws != null && "0".equals(ws.getErrcode())){
            return true;
        }else{
            return false;
        }
    }

}
public class EmailUtil {

    /**
     * 调用qq发送邮件
     * @param paranMap
     * @throws AddressException
     * @throws MessagingException
     */
    public static void sendQQEmail(QqEmailObject paranMap) throws AddressException, MessagingException {
        Properties properties = new Properties();
        properties.put("mail.transport.protocol", "smtp");// 连接协议
        properties.put("mail.smtp.host", "smtp.qq.com");// 主机名
        properties.put("mail.smtp.port", 465);// 端口号
        properties.put("mail.smtp.auth", "true");
        properties.put("mail.smtp.ssl.enable", "true");// 设置是否使用ssl安全连接 ---一般都使用
        properties.put("mail.debug", "true");// 设置是否显示debug信息 true 会在控制台显示相关信息
        //得到回话对象
        Session session = Session.getInstance(properties);
        //获取邮件对象
        Message message = new MimeMessage(session);
        // 设置发件人邮箱地址
        message.setFrom(new InternetAddress(paranMap.getSendAddress()));
        // 设置收件人邮箱地址,多个收件人之间使用
        // message.setRecipients(Message.RecipientType.TO, new InternetAddress[]{new InternetAddress("xxx@qq.com"),new InternetAddress("xxx@qq.com"),new InternetAddress("xxx@qq.com")});
        message.setRecipient(Message.RecipientType.TO, new InternetAddress(paranMap.getAcceptAddress()));//一个收件人
        // 设置邮件标题
        message.setSubject(paranMap.getTitle());
        // 设置邮件内容
        message.setText(paranMap.getEmailContent());
        // 得到邮差对象
        Transport transport = session.getTransport();
        // 连接自己的邮箱账户
        transport.connect(paranMap.getSendAddress(), paranMap.getToken());// 密码为QQ邮箱开通的stmp服务后得到的客户端授权码
        // 发送邮件
        transport.sendMessage(message, message.getAllRecipients());
        transport.close();
    }

    /**
     * 使用网易的163邮箱
     * @param netEasyEmailOnject
     */
    public static void sendNetEasyEmail(NetEasyEmailOnject netEasyEmailOnject) {
        Properties props = new Properties();
        props.put("mail.smtp.host", "smtp.163.com");//设置发送邮件的邮件服务器的属性(这里使用网易的smtp服务器)
        props.put("mail.smtp.auth", "true");  //需要经过授权,也就是有户名和密码的校验,这样才能通过验证(一定要有这一条)
        Session session = Session.getDefaultInstance(props);//用props对象构建一个session
        session.setDebug(true);
        MimeMessage message = new MimeMessage(session);//用session为参数定义消息对象
        try {
            message.setFrom(new InternetAddress(netEasyEmailOnject.getSendAddress()));// 加载发件人地址
            InternetAddress[] sendTo = new InternetAddress[netEasyEmailOnject.getAcceptsAddress().length]; // 加载收件人地址
            for (int i = 0; i < netEasyEmailOnject.getAcceptsAddress().length; i++) {
                sendTo[i] = new InternetAddress(netEasyEmailOnject.getAcceptsAddress()[i]);
            }
            message.addRecipients(Message.RecipientType.TO,sendTo);
            message.addRecipients(MimeMessage.RecipientType.CC, InternetAddress.parse(netEasyEmailOnject.getSendAddress()));//设置在发送给收信人之前给自己(发送方)抄送一份,不然会被当成垃圾邮件,报554错
            message.setSubject(netEasyEmailOnject.getTitle());//加载标题
            Multipart multipart = new MimeMultipart();//向multipart对象中添加邮件的各个部分内容,包括文本内容和附件
            BodyPart contentPart = new MimeBodyPart();//设置邮件的文本内容
            contentPart.setText(netEasyEmailOnject.getNetEastContent());
            multipart.addBodyPart(contentPart);
            if(netEasyEmailOnject.getAffixAddress()!=null && !netEasyEmailOnject.getAffixAddress().isEmpty()){//添加附件
                BodyPart messageBodyPart = new MimeBodyPart();
                DataSource source = new FileDataSource(netEasyEmailOnject.getAffixAddress());
                messageBodyPart.setDataHandler(new DataHandler(source));//添加附件的内容
                sun.misc.BASE64Encoder enc = new sun.misc.BASE64Encoder();//添加附件的标题
                messageBodyPart.setFileName("=?GBK?B?"+ enc.encode(netEasyEmailOnject.getAffixName().getBytes()) + "?=");
                multipart.addBodyPart(messageBodyPart);
            }
            message.setContent(multipart);//将multipart对象放到message中
            message.saveChanges(); //保存邮件
            Transport transport = session.getTransport("smtp");//发送邮件
            transport.connect("smtp.163.com", netEasyEmailOnject.getUser(), netEasyEmailOnject.getToken());//连接服务器的邮箱
            transport.sendMessage(message, message.getAllRecipients());//把邮件发送出去
            transport.close();//关闭连接
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

6.对接收的json字符串转为自定义的java对象做字段映射处理,将java对象转为json字符串处理(此处使用到了jackson的jar包,由于网传阿里的fastjson毛病比较多,再加上个人实际工作中,在使用阿里的fastjson打印日志的时候遇到日志打印信息处理不当的情形)

  • 1)将实体对象转为json字符串,使用new ObjectMapper().writeValueAsString(实体对象)可以转为json字符串
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

WeChatSendMessage weChatSendMessage = new WeChatSendMessage();
weChatSendMessage.setTouser(FileParamValue.getTouser());
weChatSendMessage.setMsgtype(ParamConstant.MSGTYPE_TEXT);
weChatSendMessage.setText(text);
String requestJson = null;
try {
    requestJson = new ObjectMapper().writeValueAsString(weChatSendMessage);
    logger.info("发送消息请求json报文:{}",requestJson);
} catch (JsonProcessingException e) {
    logger.error("将实体类映射为json字符串失败:{}",weChatSendMessage.toString());
}
  • 2)将json字符串与实体对象

先定义对象类,字段属性使用@JsonProperty(value = "Code"),可以将传过来的json字符串中{"Code":"123"}与实体类中的加注解的属性相映射,反之也是,若对象中属性username使用此注解@JsonProperty(value = "user_name"),在后期实体对象定义String username = "小明";后,转json字段串后将是{"user_name":"小明"}

import com.fasterxml.jackson.annotation.JsonProperty;

public class BankExamCerficate {
    @JsonProperty(value = "Code")
    private String code;

    @JsonProperty(value = "Message")
    private String message;

    @JsonProperty(value = "Data")
    private List<Data> data;

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public List<Data> getData() {
        return data;
    }

    public void setData(List<Data> data) {
        this.data = data;
    }

    @Override
    public String toString() {
        return "BankExamCerficate{" +
                "code='" + code + '\'' +
                ", message='" + message + '\'' +
                ", data=" + data +
                '}';
    }
}

接下来就是json字符串与实体类想映射(此处便于理解使用伪代码)

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false);
try {
    String emsData = "{\"Code\":0,\"Data\":[\"EMS\":\"\",\"Status\":10,\"emsInfos\":[{\"CertBatchNo\":\"20200601\",\"EMS\":\"\",\"Email\":\"\",\"Province\":\"987\",\"ProvinceName\":\"江苏省盐城市\",\"Status\":10}]}],\"Message\":\"邮寄证书信息获取成功。\"}";
     BankExamCerficate cerficate = objectMapper.readValue(emsData,BankExamCerficate.class);
     logger.info("返回报文ceficate的data信息:{}",cerficate.getData().get(0));
     logger.info("返回报文中data的ems状态:{},ems的值:{}",cerficate.getData().get(0).getStatus(),cerficate.getData().get(0).getEms());
} catch (JsonProcessingException e) {
    logger.error(e.getMessage());
}

注意:若传过来的json字符串属性较多,而java对象没有定义该对象的属性的时候,json字符串会报异常Unrecognized field xxx , not marked as ignorable。。。这是因为MappingJacksonHttpMessageConverter默认要求必须存在相应的字段。如果没有前台传来的某个字段,就会报错。

解决方法有几种,我代码中的就是一种方法,当然也可以编写全局的控制类,设置全局的objectMapper,视业务需求而定

objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false);

方法二:

在对应需要json转换的类上加上@JsonIgnoreProperties(ignoreUnknown = true)注解用来忽略不存在的字段,可以满足当前的需要。

@JsonIgnoreProperties(ignoreUnknown = true)

 方法三:

在类上加上注解,可以指定要忽略的字段@JsonIgnoreProperties({ “data”, “message” })

@JsonIgnoreProperties({ “data”, “message” })

6.编写启动类,设置定时任务,由于只是小功能,不是繁杂的业务需求,没有使用复杂的定时任务,直接使用java原生自带的定时任务;当然若是复杂的业务可以考虑使用quartz定时任务,可以参考https://mp.csdn.net/console/editor/html/105012749这篇文章

public class AppStart {
    public static void main(String[] args) {

        //此处也可以使用定时任务的jar包quartz实现
        Timer ti = new Timer();
        //在3秒后执行MyTask类中的run方法,后面每10秒跑一次
        ti.schedule(new QueryTask(), 3000,10000);

    }
}

class QueryTask extends TimerTask{
    @Override
    public void run() {
        //需要定时执行的方法
        new EMSService().boolEmsStatusForWeChatRemind();
    }
}

7.由于是定时任务,即使获取到了邮件状态,像微信,邮箱发送了信息,总不能一直提醒吧,设置一个结束条件,提醒5次,程序退出

我的解决思路是使用配置文件,默认0次,提醒一次,次数值加一,达到5的时候程序退出

接下来就是编写代码读取,修改配置文件了

public class CalcTime {
    private static Logger logger = LoggerFactory.getLogger(CalcTime.class);

    public static final Properties prop = new Properties();
    public static final String path = "calcTimes.properties";

    /**
     * 通过类加载器 初始化Properties
     */
    public static void init(){
        //转换成流
        InputStream is =CalcTime.class.getClassLoader().getResourceAsStream(path);
        try {
            //从流中读取属性列表(键和元素对
            prop.load(is);
        } catch (IOException e) {
            logger.error(e.getMessage());
        }
    }

    /**
     * 通过key获取value
     * @param key
     * @return
     */
    public static String get(String key) {
        return prop.getProperty(key);
    }

    /**
     * 修改或者新增key
     * @param key
     * @param value
     */
    public static void update(String key, String value) {
        prop.setProperty(key, value);
        FileOutputStream oFile = null;
        try {
            oFile = new FileOutputStream(path);
            //将Properties中的属性列表(键和元素对)写入输出流
            prop.store(oFile, "");
        } catch (IOException e) {
            logger.error(e.getMessage());
        } finally {
            try {
                oFile.close();
            } catch (IOException e) {
                logger.error(e.getMessage());
            }
        }
    }

    /**
     * 通过key删除value
     * @param key
     */
    public static void delete(String key) {
        prop.remove(key);
        FileOutputStream oFile = null;
        try {
            oFile = new FileOutputStream(path);
            prop.store(oFile, "");
        } catch (IOException e) {
            logger.error(e.getMessage());
        } finally {
            try {
                oFile.close();
            } catch (IOException e) {
                logger.error(e.getMessage());
            }
        }
    }

    /**
     * 循环所有key value
     */
    public static Map<String,String> list() {
        Enumeration en = prop.propertyNames(); //得到配置文件的名字
        Map<String,String> valMap = new HashMap<>();
        while(en.hasMoreElements()) {
            String strKey = (String) en.nextElement();
            String strValue = prop.getProperty(strKey);
            valMap.put(strKey,strValue);
        }
        return valMap;
    }
}

此处只是一个工具类的效果,而且程序启动需要将文件读取到内存中,所以需要在启动类中初始化配置文件

之间也有对请求报文做了测试都成功

好了所用都准备完毕,打包项目部署到服务器,坐等邮寄信息发送,哈哈!!!

 

哈哈,终于收到了,没问题啊,哈哈

 

 

 

 

 

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值