前段时间参加了一场考试,考试的成绩分数下来,当时有过想法去写的代码监听考试成绩的公布,以及调用下微信公众号通知服务,通知自己的成绩公布时间,然后懒得写,现在成绩公布通过,填写证书的邮寄地址后,需要漫长的时间等待官方证书制作,以及证书邮寄状态,以及官方提供我的证书邮寄单号信息(因为我的证书地址填写的是老家的地址),我不可能每天登录查看自己的证书以及邮寄状态,因此编写个程序去监听自己的证件邮寄状态,然后微信公众号和邮箱双重提醒
源码已上传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方式
注意:具体参考微信开发说明文档,一切以官方文档为准:
报文案例如下:
{
"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;
}
}
此处只是一个工具类的效果,而且程序启动需要将文件读取到内存中,所以需要在启动类中初始化配置文件
之间也有对请求报文做了测试都成功
好了所用都准备完毕,打包项目部署到服务器,坐等邮寄信息发送,哈哈!!!
哈哈,终于收到了,没问题啊,哈哈