公司要求实现微信公众号的一些功能,本人经过查阅资料发现使用weixin.java.tools对微信进行开发十分便捷,遂将完成的几个小功能贴出来进行记录。
主要接入步骤:
- 通过ngrok获取动态测试域名。(如果有的话可以用自己的域名,如果想要免费使用域名的话建议使用ngrok内网穿透工具。它的缺点是每次运行ngrok都会变更域名,免费的要求也不要这么多啦)
- 申请测试公众号。(想要使用微信服务器提供的所有接口的话需要认证公众号,认证公众号一年掏三百多这里不建议)
- 将微信端与本地服务器端进行对接。
开发工具:idea
其他工具 :ngrok(本文含下载链接),测试公众号(官网注册)
1.注册公众号:
2.运行ngrok内网穿透工具
输入命令行:ngrok 8080(如不行输入ngrok http 8080)
若运行成功则会动态分配一个域名
3.创建Springboot+Maven项目(如不会的自行百度)
4.引入Maven依赖
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>weixin</artifactId>
<packaging>jar</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>weixin</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-mp</artifactId>
<version>3.4.9.B</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.8.RELEASE</version>
</dependency>
<dependency>
<groupId>net.minidev</groupId>
<artifactId>json-smart</artifactId>
<version>2.3</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.31</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
其中引入的weixin-java-mp依赖就是我们今天的主角,weixin-java-tools是大神binarywang写的开发工具包,有了它我们将减少很多底层的工作量,如token的加密,accesstoken的周期管理,传入传出数据的封装与解析等都不用我们亲自动手完成:
<dependency> <groupId>com.github.binarywang</groupId> <artifactId>weixin-java-mp</artifactId> <version>3.4.9.B</version> </dependency>
5.进行微信服务器端与本地服务器端的对接工作(代码模块正式开始)
配置文件
server.port = 8080
server.servlet.context-path = /weixin
mpAppId= #公众号id
mpAppSecret= #公众号密匙
token=smk666
其中公众号的信息在测试号里面都有:
微信服务器对接代码块
package com.example.demo.web;
@SpringBootApplication
@Controller
public class LoginController {
@Value("${token}")//获取配置文件的值
private String token;
@RequestMapping(value = "/wx",method= RequestMethod.GET)
public void login(HttpServletRequest request, HttpServletResponse response) throws IOException {
System.out.println(token);
//微信服务器get传递的参数
String signature = request.getParameter("signature");//微信加密签名
String timestamp = request.getParameter("timestamp");//时间戳
String nonce = request.getParameter("nonce");//随机数
String echostr = request.getParameter("echostr");//随机字符串(用来进行返回)
//微信工具类
WxMpService wxService=new WxMpServiceImpl();
//注入token的配置参数
/**
* 生产环境 建议将WxMpInMemoryConfigStorage持久化
*/
WxMpInMemoryConfigStorage wxConfigProvider=new WxMpInMemoryConfigStorage();
//注入token值
wxConfigProvider.setToken(token);
wxService.setWxMpConfigStorage(wxConfigProvider);
boolean flag=wxService.checkSignature(timestamp, nonce, signature);
System.out.println(flag);
PrintWriter out=response.getWriter();
if(flag){
out.print(echostr);
}
out.close();
}
}
写完后在公众号测试号端进行测试,需要重点说明的是URL。
如以下URL:http://www.wx.fzy365.online(域名)+/weixin/wx(对接接口的地址)
测试成功后我们进行下一步
6.模板推送功能的实现
新增测试模板
其中模板ID用于我们后台接口的调用,模板内容为键值对形式,key1、key2为传入参数的键值且后缀加.DATA。
示例:姓名 {{key1.DATA}}
获取Accesstoken
调用微信接口必须得获取调用权限,其中Accesstoken就是该权限的凭证,两小时刷新一次,weixin.java.tool中已经封装好相关的管理方法,这里我将获取Accesstoken部分的代码单独写成一个工具类以便调用:
package com.example.demo.Util;
import me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
import java.io.IOException;
import java.util.Properties;
/**
* @说明:
* @创建人:孙明康
* @创建时间:2019/8/1 15:57
*/
public class GetAccessTokenUtil {
/**
* @Description:根据微信公众号id,secret获取access_token
* @return: wxMpService(封装好的服务,内含调用微信接口必须的access_token)
* @author: 孙明康 2019/8/3 17:06
*/
private static Properties properties;
public static WxMpService getAccessToken() {
//获取配置信息中的公众号信息
if (properties==null) {
properties = new Properties();
ClassLoader loader = Thread.currentThread().getContextClassLoader();
try {
properties.load(loader.getResourceAsStream("application.properties"));
} catch (IOException e) {
e.printStackTrace();
}
}
String mpAppId = properties.getProperty("mpAppId");
String mpAppSecret =properties.getProperty("mpAppSecret");
WxMpInMemoryConfigStorage wxStorage = new WxMpInMemoryConfigStorage();
wxStorage.setAppId(mpAppId);
wxStorage.setSecret(mpAppSecret);
WxMpService wxMpService = new WxMpServiceImpl();
wxMpService.setWxMpConfigStorage(wxStorage);
return wxMpService;
}
}
模版消息推送功能的代码实现
传入值为json格式,注释@param里面有写(后面的controller同理):
package com.example.demo.web;
import java.util.List;
/**
* @说明:模版消息推送
*/
@CrossOrigin
@RestController
public class PushController {
/**
* @Description:模板消息推送
* @param:目标用户id,推送模板id,模板信息列表:{'openid':'oopKVw3CjDqczvJ9fLSecbYZYl14','templateId':'eBcuETQZjL_R4ZzLd6L9rIehSiSGGCtWe_xpWqBkMnI','returnUrl':'www.baidu.com/','wxMpTemplateDataList':[{'name':'key1','value':'孙明康','color':'#FF00FF'},{'name':'key2','value':'您的服务器有新的预警情况哦','color':'#FF00FF'}]}
* @return:状态返回值:{"statusMessage":true}
* @author: 孙明康 2019/8/3 16:20
*/
@PostMapping("/push")
//正式发布public ApiResult push(String jsonString) throws WxErrorException {
public ApiResult push(@RequestBody String jsonString) throws WxErrorException {
String openid = null;
String templateId= null;
String returnUrl = null;
String templateJsonArray = null;
List<WxMpTemplateData> wxMpTemplateDataList = null;
JSONObject jsonObj = JSONObject.parseObject(jsonString);
if(null != jsonObj) {
openid = jsonObj.getString("openid");
templateId = jsonObj.getString("templateId");
returnUrl = jsonObj.getString("returnUrl");
//JSONArray templateJsonArray = jsonObj.getJSONArray("wxMpTemplateDataList");
templateJsonArray = jsonObj.getString("wxMpTemplateDataList");
wxMpTemplateDataList=JSONArray.parseArray(templateJsonArray,WxMpTemplateData.class);
}
//access_token为获取的接口调用taken
WxMpService access_token = GetAccessTokenUtil.getAccessToken();
//设置推送对象信息
WxMpTemplateMessage templateMessage = WxMpTemplateMessage.builder()
//
.toUser(openid)//要推送的用户openid
.templateId(templateId)//模版id
.url(returnUrl)//点击模版消息要访问的网址
.build();
// 模板中的map值templateMessage.addData(new WxMpTemplateData(name2, value2, color2));
String name = null;
String value = null;
String color = null;
System.out.println("获取templateId的值----"+templateId);
System.out.println("获取openid的值----"+openid);
System.out.println("获取returnUrl的值----"+returnUrl);
for(int i = 0;i < wxMpTemplateDataList.size(); i ++){
WxMpTemplateData wxMpTemplateDataEx = wxMpTemplateDataList.get(i);
name = wxMpTemplateDataEx.getName();
value = wxMpTemplateDataEx.getValue();
color = wxMpTemplateDataEx.getColor();
templateMessage.addData(new WxMpTemplateData(name, value, color));
}
boolean statusMessage = true;
try {
//调用
String zhaungtai = access_token.getTemplateMsgService().sendTemplateMsg(templateMessage);
System.out.println("若传输成功返回的状态是"+zhaungtai);
} catch (Exception e) {
System.out.println("推送失败:" + e.getMessage());
statusMessage = false;
e.printStackTrace();
}finally {
JSONObject jsonObject = new JSONObject();
jsonObject.put("statusMessage",statusMessage);
return ResultApiUtils.getSuccessResult(jsonObject);
}
}
}
7.客服信息功能(主动向用户发送信息)功能的代码实现
这个功能比较鸡肋,如果客户在48小时之内没有跟你的公众号说过话就没法调用。
package com.example.demo.web;
/**
* @说明:主动发送消息(客服消息)
* @创建人:孙明康
* @创建时间:2019/8/2 14:30
*/
@CrossOrigin
@RestController
public class KefuMessageController {
/**
* @Description:微信测试账号推送
* @param:用户id及向用户发送的具体消息{openid:'oopKVw3CjDqczvJ9fLSecbYZYl14',content:'测试:\n超链接:<a href="http://www.4399.com/">预警情况</a>'}
* @return:状态返回值:{"statusMessage":true}
* @author: 孙明康 2019/8/3 16:47
*/
@PostMapping("/kefu")
// 正式发布public ApiResult kefu(String jsonString) {
public ApiResult kefu(@RequestBody String jsonString) {
String openid = null;
String content = null;
JSONObject jsonObj = JSONObject.parseObject(jsonString);
if(null != jsonObj) {
openid = jsonObj.getString("openid");
content = jsonObj.getString("content");
}
//access_token为获取的接口调用accesstaken(接口自动刷新)
WxMpService access_token = GetAccessTokenUtil.getAccessToken();
//设置推送的具体信息
WxMpKefuMessage message = new WxMpKefuMessage();
message.setMsgType(WxConsts.KefuMsgType.TEXT);
message.setToUser(openid);
//测试:\n超链接:<a href="http://www.4399.com/">预警情况</a>
message.setContent(content);
boolean statusMessage = true;
try {
//getAccessTokenUtil.getAccessToken()用来设置accesstoken
access_token.getKefuService().sendKefuMessage(message);
} catch (Exception e) {
System.out.println("推送失败:" + e.getMessage());
statusMessage = false;
e.printStackTrace();
}finally {
JSONObject jsonObject = new JSONObject();
jsonObject.put("statusMessage",statusMessage);
return ResultApiUtils.getSuccessResult(jsonObject);
}
}
}
8.客户信息列表的拉取
这里我调用了两个微信服务器提供的接口,分别为客户openID的批量拉取接口、单个客户信息的获取接口。
package com.example.demo.web;
/**
* @说明:
* @创建人:孙明康
* @创建时间:2019/8/2 15:16
*/
@CrossOrigin
@RestController
public class WxUserController {
/**
* @Description:从微信端获取用户ID和个人信息
* @param:拉取所有用户:{next_openid:''}or从该id向下进行拉取(不包含改id):{next_openid:'oopKVw10zjn0cqTCapx1wKY9WYF8'}
* @return:用户信息列表userList:[{"subscribe":true,"openId":"oopKVw3CjDqczvJ9fLSecbYZYl14","nickname":"孙明康","sexDesc":"男","sex":1,"language":"zh_CN","city":"珠海","province":"广东","country":"中国","headImgUrl":"http://thirdwx.qlogo.cn/mmopen/1kc9BKglZaKic1etEeibLCvia5qSaz7tO5MePZe7BPtTxibXcWPRc6KMeFa7KatdW8Sm9BncY54vbdxWVvEwJO6Hbxy42q7z0HuC/132","subscribeTime":1564645086,"remark":"","groupId":0,"tagIds":[],"subscribeScene":"ADD_SCENE_QR_CODE","qrScene":"0","qrSceneStr":""}]
* @author: 孙明康 2019/8/2 15:21
*/
@GetMapping("/wxUser")
//正式发布public ApiResult wxUser(String jsonString) throws WxErrorException {
public ApiResult wxUser(@RequestBody String jsonString) throws WxErrorException {
String next_openid = "";
JSONObject jsonObj = JSONObject.parseObject(jsonString);
if(null != jsonObj) {
next_openid = jsonObj.getString("next_openid");
}
//access_token为获取的接口调用accesstaken(接口自动刷新)
WxMpService access_token = GetAccessTokenUtil.getAccessToken();
//获取存储在微信端的用户个人信息
WxMpUserList wxUserList = access_token.getUserService().userList(next_openid);
//获取用户信息
List<WxMpUser> userList = new ArrayList();
for(String userOpenId : wxUserList.getOpenids()) {
String lang = "zh_CN"; //语言
WxMpUser userMessage = access_token.getUserService().userInfo(userOpenId, lang);
System.out.println("用户的个人信息:——————" + userMessage);
userList.add(userMessage);
}
System.out.println("用户信息列表:——————" + userList);
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("userList",userList);
return ResultApiUtils.getSuccessResult(resultMap);
}
}
9.一些封装json的类
APIResult
package com.example.demo.entity;
/**
* @说明:
* @创建人:孙明康
* @创建时间:2019/8/3 10:21
*/
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import java.io.Serializable;
import java.util.Calendar;
import java.util.Date;
import java.io.Serializable;
import java.util.Calendar;
import java.util.Date;
public class ApiResult implements Serializable {
private static final long serialVersionUID = -7595446147358370950L;
private int status;
private String message;
private Object data;
private Date requesttime = Calendar.getInstance().getTime();
public ApiResult() {
}
public Date getRequesttime() {
return this.requesttime;
}
public ApiResult setRequesttime(Date requesttime) {
this.requesttime = requesttime;
return this;
}
public int getStatus() {
return this.status;
}
public ApiResult setStatus(int status) {
this.status = status;
return this;
}
public String getMessage() {
return this.message;
}
public ApiResult setMessage(String message) {
this.message = message;
return this;
}
public Object getData() {
return this.data;
}
public ApiResult setData(Object data) {
this.data = data;
return this;
}
}
ResultApiUtils
package com.example.demo.entity;
/**
* @说明:
* @创建人:孙明康
* @创建时间:2019/8/3 10:24
*/
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import com.alibaba.fastjson.JSONObject;
import org.springframework.http.HttpStatus;
import com.example.demo.entity.ApiResult;
public class ResultApiUtils {
private static final String DEFAULT_SUCCESS_MESSAGE = "success";
private static final String DEFAULT_FAIL_MESSAGE = "fail";
public ResultApiUtils() {
}
public static ApiResult getDefaultResult() {
return new ApiResult();
}
public static ApiResult getSuccessResult() {
return (new ApiResult()).setStatus(HttpStatus.OK.value()).setMessage("success");
}
public static ApiResult getSuccessResult(Object data) {
return (new ApiResult()).setStatus(HttpStatus.OK.value()).setMessage("success").setData(data);
}
public static ApiResult getSuccessResult(int statusCode, String message, Object data) {
return null == message?(new ApiResult()).setStatus(statusCode).setMessage("success").setData(data):(null == data?(new ApiResult()).setStatus(statusCode).setMessage(message):(new ApiResult()).setStatus(statusCode).setMessage(message).setData(data));
}
public static ApiResult getFailResult() {
return (new ApiResult()).setStatus(HttpStatus.BAD_REQUEST.value()).setMessage("fail");
}
public static ApiResult getFailResult(String message) {
return (new ApiResult()).setStatus(HttpStatus.BAD_REQUEST.value()).setMessage(message);
}
public static ApiResult getFailResult(int statusCode, String message) {
return null == message?(new ApiResult()).setStatus(statusCode).setMessage("success"):(new ApiResult()).setStatus(statusCode).setMessage(message);
}
public static ApiResult getFailResult(int statusCode, String message, Object data) {
return null == message?(new ApiResult()).setStatus(statusCode).setMessage("success").setData(data):(null == data?(new ApiResult()).setStatus(statusCode).setMessage(message):(new ApiResult()).setStatus(statusCode).setMessage(message).setData(data));
}
public static String resultToJson(ApiResult apiResult) {
return JSONObject.toJSONString(apiResult);
}
}
10.测试
好不容易就都写完啦,这里就展示一个模板消息推送接口的测试,其余测试同理。
PushControllerTest
package com.example.demo.Test;
/**
* @说明:
* @创建人:孙明康
* @创建时间:2019/8/1 16:51
*/
import com.example.demo.entity.ApiResult;
import com.example.demo.web.PushController;
import me.chanjar.weixin.common.error.WxErrorException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import static org.junit.Assert.*;
@RunWith(SpringRunner.class)
@SpringBootTest
public class PushControllerTest {
@Autowired
PushController pushController;
@Test
public void push() throws WxErrorException {
ApiResult status=pushController.push("{'openid':'oopKVw3CjDqczvJ9fLSecbYZYl14','templateId':'eBcuETQZjL_R4ZzLd6L9rIehSiSGGCtWe_xpWqBkMnI','returnUrl':'www.baidu.com/','wxMpTemplateDataList':[{'name':'key1','value':'孙明康','color':'#FF00FF'},{'name':'key2','value':'您的服务器有新的预警情况哦','color':'#FF00FF'}]}");
System.out.println(status.getData());
}
}
11.小结
到这里本次微信接口demo的编写就全部完成辽,感觉目前网上面关于weixin.java.tool工具包的介绍不是很多,如果想自学的话建议去GitHub上面直接搜开发文档看。