目录
相关文档
公众号获取access_token
https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html
新增永久素材(包括新增其它永久素材和新增永久图文素材等接口)
https://developers.weixin.qq.com/doc/offiaccount/Asset_Management/Adding_Permanent_Assets.html
群发接口
业务流程
- 准备参数:1文章内容(可以是纯文本或者带html),2封面图片
- 将图片上传至”新增其它永久素材“接口获取该图片的media_id(该图片会上传至公众号的图片素材库,可在公众号后台查询)
- 将封面图片的media_id和文章内容(注意如果内容带html需要注意<>等符号是否被转义,否则会出现html不被解析的情况)等参数调用新增永久图文素材接口获取图文素材的media_id()(该文章会上传至公众号的图文素材库,可在公众号后台查询)
- 将图文素材库的media_id调用群发接口推送给用户(可选随送给所有用户或者指定标签用户)(每月推送次数有限)
代码实现
引入依赖
<!--httpclient工具类https://github.com/Arronlong/httpclientutil-->
<dependency>
<groupId>com.arronlong</groupId>
<artifactId>httpclientutil</artifactId>
<version>1.0.4</version>
</dependency>
yml参数
wechat:
appid: wx2c4d9cfa5767575f
secret: 9396f2ba7cdab92bad13c5ad69c76f88
# 获取token的url
access_token_url: https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential
# 保存永久图文素材url
save_permanent_image_text_material: https://api.weixin.qq.com/cgi-bin/material/add_news?access_token=
# 保存永久其它素材url
save_permanent_other_material: https://api.weixin.qq.com/cgi-bin/material/add_material?access_token=
# 群发素材到用户url
tag_send_material_url: https://api.weixin.qq.com/cgi-bin/message/mass/sendall?access_token=
# 生成图文素材的阅读原文地址
content_source_url: http://www.gacycu.cn/
微信相关接口调用封装
package com.snow.management.plug.wechat;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.arronlong.httpclientutil.HttpClientUtil;
import com.arronlong.httpclientutil.common.HttpConfig;
import com.arronlong.httpclientutil.exception.HttpProcessException;
import com.snow.management.util.EhCacheUtil;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import java.util.*;
/**
* 微信公众号相关
* @ClassName Wechat
* @Author snow
* @Date 2020/10/19 14:38
*/
public class Wechat {
private String appId = "";
private String secret = "";
private String tokenUrl = "";
public Wechat(String appId, String secret, String tokenUrl) {
this.appId = appId;
this.secret = secret;
this.tokenUrl = tokenUrl;
}
/**
* 获取token
*/
public String getAccessToken() throws HttpProcessException {
String result = HttpClientUtil.get(HttpConfig.custom().url(tokenUrl + "&appid=" + appId + "&secret=" + secret));
JSONObject resultObj = JSON.parseObject(result);
if (!resultObj.containsKey("access_token")) {
throw new RuntimeException(result);
}
String token = resultObj.getString("access_token");
EhCacheUtil.getInstance().put(EhCacheUtil.LOGIN_CACHE, "access_token", token);
EhCacheUtil.getInstance().put(EhCacheUtil.LOGIN_CACHE, "access_token_time", System.currentTimeMillis());
return token;
}
/**
* 判断token是否有效
* @return 有效返回token
*/
private String validToken() {
String token = (String) EhCacheUtil.getInstance().get(EhCacheUtil.LOGIN_CACHE, "access_token");
if (token == null) {
return "";
}
long validTime = (long) EhCacheUtil.getInstance().get(EhCacheUtil.LOGIN_CACHE, "access_token_time");
if (validTime + 7000000 >= System.currentTimeMillis()) {
return token;
}
return "";
}
/**
* 新增永久图文素材
* @param imageTextMaterialUrl 新增素材的url
* @param title 标题
* @param thumbMediaId 封面图片的media_id
* @param digest 摘要
* @param showCoverPic 是否显示封面
* @param content 内容
* @param contentSourceUrl 阅读原文地址
* @return 图文素材的media_id
*/
public String savePermanentImageTextMaterial(String imageTextMaterialUrl, String title, String thumbMediaId, String digest
, int showCoverPic, String content, String contentSourceUrl) throws HttpProcessException {
String token = validToken();
if (token.isEmpty()) {
token = getAccessToken();
}
Map<String, Object> article = new LinkedHashMap<>(5);
// 标题
article.put("title", title);
// 图文消息的封面图片素材id(必须是永久mediaID)
article.put("thumb_media_id", thumbMediaId);
// 图文消息的摘要,仅有单图文消息才有摘要,多图文此处为空。如果本字段为没有填写,则默认抓取正文前64个字。
article.put("digest", digest);
// 是否显示封面,0为false,即不显示,1为true,即显示
article.put("show_cover_pic", showCoverPic);
// 图文消息的具体内容,支持HTML标签,必须少于2万字符,小于1M,且此处会去除JS,涉及图片url必须来源 "上传图文消息内的图片获取URL"接口获取。
// 外部图片url将被过滤。
article.put("content", content);
// 图文消息的原文地址,即点击“阅读原文”后的URL
article.put("content_source_url", contentSourceUrl);
List<Map<String, Object>> list = new ArrayList<>();
list.add(article);
Map<String, Object> param = new HashMap<>(1);
param.put("articles", list);
System.out.println(param);
HttpConfig config = HttpConfig.custom().url(imageTextMaterialUrl + token).encoding("utf-8").json(JSONObject.toJSONString(param));
String resultStr = HttpClientUtil.post(config);
JSONObject result = JSONObject.parseObject(resultStr);
if (!result.containsKey("media_id")) {
throw new RuntimeException(resultStr);
}
System.out.println("图文素材media_id:" + result.getString("media_id"));
return result.getString("media_id");
}
/**
* 新增永久其它素材
* @param imageOtherUrl 上传地址
* @param type 媒体文件类型,分别有图片(image)、语音(voice)、视频(video)和缩略图(thumb)
* @param filePath 要上传的文件地址
* @return 素材的mediaId
* @throws HttpProcessException
*/
public String savePermanentOtherMaterial(String imageOtherUrl, String type, String filePath) throws HttpProcessException {
String token = validToken();
if (token.isEmpty()) {
token = getAccessToken();
}
MultiValueMap<String, Object> data = new LinkedMultiValueMap<>();
//设置上传文件
FileSystemResource fileSystemResource = new FileSystemResource(filePath);
data.add("media", fileSystemResource);
//上传文件,设置请求头
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
httpHeaders.setContentLength(fileSystemResource.getFile().length());
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(data,
httpHeaders);
RestTemplate restTemplate = new RestTemplate();
String resultString = restTemplate.postForObject(imageOtherUrl + token + "&type=" + type, requestEntity, String.class);
JSONObject result = JSONObject.parseObject(resultString);
if (!result.containsKey("media_id")) {
throw new RuntimeException(resultString);
}
System.out.println("其它素材media_id:" + result.getString("media_id"));
return result.getString("media_id");
}
/**
* 根据标签进行群发【订阅号与服务号认证后均可用】
* @param url 推送接口
* @param mediaId 图文素材id
* @throws HttpProcessException
*/
public String sendImageTextMessage(String url, String mediaId) throws HttpProcessException {
String token = validToken();
if (token.isEmpty()) {
token = getAccessToken();
}
JSONObject filter = new JSONObject();
// 用于设定是否向全部用户发送,值为true或false,选择true该消息群发给所有用户,选择false可根据tag_id发送给指定群组的用户
filter.put("is_to_all", true);
// 群发到的标签的tag_id,参见用户管理中用户分组接口,若is_to_all值为true,可不填写tag_id
// filter.put("tag_id", 2);
JSONObject mpnews = new JSONObject();
// 用于群发的消息的media_id
mpnews.put("media_id", mediaId);
JSONObject param = new JSONObject();
// 用于设定图文消息的接收者
param.put("filter", filter);
// 用于设定即将发送的图文消息
param.put("mpnews", mpnews);
// 群发的消息类型,图文消息为mpnews,文本消息为text,语音为voice,音乐为music,图片为image,视频为video,卡券为wxcard
param.put("msgtype", "mpnews");
// 图文消息被判定为转载时,是否继续群发。 1为继续群发(转载),0为停止群发。 该参数默认为0。
param.put("send_ignore_reprint", 0);
HttpConfig config = HttpConfig.custom().url(url + token).encoding("utf-8").json(JSONObject.toJSONString(param));
String resultStr = HttpClientUtil.post(config);
JSONObject result = JSONObject.parseObject(resultStr);
Integer code = result.getInteger("errcode");
if (code == 0) {
return "";
} else if (code == 45065) {
return "文章已推送";
}
throw new RuntimeException(resultStr);
}
}
嵌入业务
/**
* 发送文章到公众号
* @param id 文章id
*/
private JSONObject shareWechat(String id) throws HttpProcessException {
if (DataOperateUtil.isNull(appid, secret, tokenUrl, imageTextMaterialUrl, otherMaterialUrl, tagSendMaterialUrl)) {
return fail("缺少参数配置");
}
QueryWrapper<Article> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("id", id);
queryWrapper.select("id", "title", "introduction", "type", "cover", "content", "media_id");
Article article = articleService.getOne(queryWrapper);
if (article == null) {
return fail("文章不存在");
}
if (article.getType() != 1) {
return fail("仅支持自行编辑的图文文章");
}
if (DataOperateUtil.isNull(article.getCover())) {
return fail("请上传封面用以公众号推送图文信息");
}
Wechat wechat = new Wechat(appid, secret, tokenUrl);
String materialMediaId = "";
// 判断文章是否已经上传到公众号素材库
if (DataOperateUtil.isNull(article.getMediaId())) {
String coverMediaId = wechat.savePermanentOtherMaterial(otherMaterialUrl, "image", fileUploadPath + article.getCover());
materialMediaId = wechat.savePermanentImageTextMaterial(imageTextMaterialUrl, article.getTitle(), coverMediaId, article.getIntroduction(), 1, article.getContent(), contentSourceUrl);
// 保存素材id到库,防止多次上传到公众号素材库
article.setMediaId(materialMediaId);
articleService.updateById(article);
} else {
materialMediaId = article.getMediaId();
}
String sendResult = wechat.sendImageTextMessage(tagSendMaterialUrl, materialMediaId);
if (sendResult.isEmpty()) {
return successNotData();
}
return fail(sendResult);
}