微信公众号-给指定用户发送信息
文章目录
前言
本篇主要讲解微信公众号中,根据openID向指定用户发送消息。
前置条件是:
①微信公众号已经被认证。
②微信公众号自定义菜单已设置。
③公众号设置->功能设置->JS接口安全域名、网页授权域名。
④已获取到AppId,AppSecret(开发->基本配置->公众号开发信息 中查看)。
⑤ip白名单已设置好(开发->基本配置->公众号开发信息->白名单 中设置)。
⑥服务器配置(开发/基本配置/ 填写服务器配置。本次章中未使用,因为功能简单,采用了链接的方式)
⑦至于根据code换token,加密签名这一些玩意就更多了,还有支付一些坑,mmp不说了
微信的这些东西,做了八百遍每次都有踩不完的坑,官方文档,emm,官方文档算了吧,mmp
鉴于找资料这么费劲, 准备写一下系列文章,从微信配置开始,包括以上全部前置条件、到使用,到服务器配置发布。 因懒取消原计划。
如果有需要的说一下,反正我也不一定写。
一、开通模板消息
看剑,不看箭头,点击开通
可以从模板库中选择合适的模板,也可以自己添加,我用【工单消息提醒】模板做例子。
{{first.DATA}}
工单编号:{{keyword1.DATA}}
工单标题:{{keyword2.DATA}}
时间:{{keyword3.DATA}}
{{remark.DATA}}
在发送时,需要将内容中的参数({{.DATA}}内为参数)赋值替换为需要的信息
二、项目使用步骤
1.引入库
- hutool工具包。
- lombok插件自己装。
- 阿里巴巴的fastjson。
代码如下(示例):
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.5.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.54</version>
</dependency>
2.直接上代码
controller
@Autowired
private WeChatMessageImpl weChatMessage;
@RequestMapping("/sendMessage")
public R sendMessage() {
try {
WeChatConfig weChatConfig = new WeChatConfig();
TemplateConfig first = new TemplateConfig();
first.setValue("first");
TemplateConfig k1 = new TemplateConfig();
k1.setValue("k1");
TemplateConfig k2 = new TemplateConfig();
k2.setValue("k2");
TemplateConfig k3 = new TemplateConfig();
k3.setValue("k3");
k3.setColor("#ff3800");//颜色可以不设置,为默认
TemplateConfig remark = new TemplateConfig();
remark.setValue("remarksadfasdf");
remark.setColor("#ff3800");
WeChatOrderNoticeVo data = new WeChatOrderNoticeVo();
data.setFirst(first);
data.setKeyword1(k1);
data.setKeyword2(k2);
data.setKeyword3(k3);
data.setRemark(remark);
MessageTemplate<WeChatOrderNoticeVo> template = new MessageTemplate<>();
template.setTemplate_id("消息模板id");
template.setTopcolor("#808080");
template.setTouser("要发送对象的openID");
template.setUrl("#");
template.setData(data);
return weChatMessage.sendMessage(weChatConfig,template);
} catch (Exception e) {
e.printStackTrace();
return R.fail("发送失败");
}
}
service
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@Service
public class WeChatMessageImpl {
private static Logger log = LoggerFactory.getLogger(WeChatMessageImpl.class);
/**
* * 发送模板消息
* * weChatConfig 微信配置
* * MessageTemplate 消息模板
* @return
*/
public R sendMessage(WeChatConfig weChatConfig, MessageTemplate<WeChatOrderNoticeVo> wechatOrderNoticeTemplate ) {
Token token = WeChatUtils.getToken(weChatConfig.getAppId(), weChatConfig.getAppSecret());
if(null == token || StrUtil.isBlank(token.getAccessToken())){
return R.fail("获取token失败");
}
String access_token = token.getAccessToken();
String url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + access_token;
String jsonString = JSONObject.toJSONString(wechatOrderNoticeTemplate).replace("day", "Day");
log.info(jsonString);
JSONObject jsonObject = WeChatUtils.httpsRequest(url, "POST", jsonString);
System.out.println(jsonObject);
if (null != jsonObject) {
if (StrUtil.isBlank(jsonObject.getString("errcode"))
|| !StrUtil.equals(jsonObject.getString("errcode"),"0")){
log.error("错误 errcode:{} errmsg:{}", jsonObject.getInteger("errcode"), jsonObject.getString("errmsg"));
return R.fail(jsonObject.toString());
}
}
return R.ok(jsonObject);
}
}
其他一些封装的类
BaseTemplateField
import lombok.Data;
/**
* @author :socilents
* @date :Created in 2020/11/4 16:41
* @description:微信模板基础字段(必填项),这些字段变量的名字不可改变,变了微信接口不认
*/
@Data
public class BaseTemplateField {
//用户OpenID
private String touser;
//模板消息ID
private String template_id;
//点击详情后要进入的页面,可为空
private String url;
//标题颜色,根据官方文档写的,好像没啥卵用
private String topcolor;
}
MessageTemplate
import lombok.Data;
/**
* @author :socilents
* @date :Created in 2020/11/4 16:39
* @description:微信发送消息模板封装,
* 同BaseTemplateField中的字段名称一样,不可以更改字段名称,否则不认
*/
@Data
public class MessageTemplate<T> extends BaseTemplateField {
private T data;
}
MyX509TrustManager
import javax.net.ssl.X509TrustManager;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
/**
* 信任管理器
*
*/
public class MyX509TrustManager implements X509TrustManager {
@Override
public void checkClientTrusted(X509Certificate[] arg0, String arg1)
throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] arg0, String arg1)
throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
TemplateConfig
import lombok.Data;
/**
* @author :magangming
* @date :Created in 2020/11/4 16:40
* @description:微信模板字段设置
*/
@Data
public class TemplateConfig {
//字段值
private String value;
//字体颜色
private String color;
}
WeChatConfig
import lombok.Data;
/**
* @author :magangming
* @date :Created in 2020/11/4 17:07
* @description:微信公众号的基本配置
* 根据实际情况使用,这么写是为了方便展示
*/
@Data
public class WeChatConfig {
private String appId="你的APPID";
private String appSecret="你的appsecret";
//为了方便展示写在这里,根据实际情况使用
private String openId;
private String templeId;
}
WeChatOrderNoticeVo
import lombok.Data;
/**
* @author :socilents
* @date :Created in 2020/11/4 16:51
* @description:发送消息的封装类,
* 工单模板内容 微信公众号通知模板
* 这里我引用了工单消息提醒的模板,记得要在消息模板中添加
*/
@Data
public class WeChatOrderNoticeVo {
private TemplateConfig first;
private TemplateConfig keyword1;
private TemplateConfig keyword2;
private TemplateConfig keyword3;
private TemplateConfig remark;
}
WeChatUtils
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.kalvin.kvf.modules.web.entity.Token;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.URL;
/**
* @author :socilents
* @date :2020/11/2 17:20
* @description: 微信公众号指定openID发送消息工具类
*/
public class WeChatUtils {
private static Logger log = LoggerFactory.getLogger(WeChatUtils.class);
// 凭证获取(GET)
public final static String token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
/**
* @param requestUrl 请求地址
* @param requestMethod 请求方式(GET、POST)
* @param outputStr 提交的数据
* @return JSONObject(通过 JSONObject.get(key) 的方式获取 JSON 对象的属性值)
* @description: 发送 https 请求,方法有多种
*/
public static JSONObject httpsRequest(String requestUrl, String requestMethod, String outputStr) {
JSONObject jsonObject = null;
try {
// 创建 SSLContext 对象,并使用我们指定的信任管理器初始化
TrustManager[] tm = {new MyX509TrustManager()};
SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
sslContext.init(null, tm, new java.security.SecureRandom());
// 从上述 SSLContext 对象中得到 SSLSocketFactory 对象
SSLSocketFactory ssf = sslContext.getSocketFactory();
URL url = new URL(requestUrl);
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(ssf);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);
// 设置请求方式(GET/POST)
conn.setRequestMethod(requestMethod);
// 当 outputStr 不为 null 时,向输出流写数据
if (null != outputStr) {
OutputStream outputStream = conn.getOutputStream();
// 注意编码格式
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
// 从输入流读取返回内容
InputStream inputStream = conn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
StringBuffer buffer = new StringBuffer();
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
// 释放资源
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
inputStream = null;
conn.disconnect();
jsonObject = JSONObject.parseObject(buffer.toString());
} catch (ConnectException ce) {
log.error(" 连接超时:{}", ce);
} catch (Exception e) {
log.error("https 请求异常:{}", e);
}
return jsonObject;
}
/**
* @author :socilents
* @date :2020/11/5 7:59
* @param appId 凭证
* @param secret 密钥
* @description: 获取接口访问凭证
*/
public static Token getToken(String appId, String secret) {
Token token = null;
String requestUrl = token_url.replace("APPID", appId).replace("APPSECRET", secret);
// 发起GET请求获取凭证
JSONObject jsonObject = httpsRequest(requestUrl, "GET", null);
if (null != jsonObject) {
if (StrUtil.isBlank(jsonObject.getString("errcode"))) {
try {
token = new Token();
token.setAccessToken(jsonObject.getString("access_token"));
token.setExpiresIn(jsonObject.getInteger("expires_in"));
log.info(jsonObject.getString("access_token"));
} catch (JSONException e) {
token = null;
// 获取token失败
log.error("获取token失败 errcode:{} errmsg:{}", jsonObject.getInteger("errcode"), jsonObject.getString("errmsg"));
}
}else {
log.error("获取token失败 errcode:{} errmsg:{}", jsonObject.getInteger("errcode"), jsonObject.getString("errmsg"));
token = null;
}
}
return token;
}
}
返回结果R的封装
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import ****************************************.Constants;
import java.io.Serializable;
/**
* 返回前端 数据封闭类
*/
public class R implements Serializable {
private static final long serialVersionUID = 1L;
private Integer code;
private String msg;
private Object data;
private Long total; // 分页信息:总条数
public R() { }
private R(int code, String msg, Object data) {
this.code = code;
this.msg = msg;
if (data instanceof Page<?>) {
Page<?> page = (Page<?>) data;
this.total = page.getTotal();
this.data = page.getRecords();
} else {
this.data = data;
}
}
public static R ok() {
return new R(Constants.OK_CODE, Constants.OK_MSG, null);
}
public static R ok(Object data) {
return new R(Constants.OK_CODE, Constants.OK_MSG, data);
}
public static R ok(String msg, Object data) {
return new R(Constants.OK_CODE, msg, data);
}
public static R fail(String msg) {
return new R(Constants.FAIL_CODE, msg, null);
}
public static R invalidToken(String msg) {
return new R(Constants.INVALID_TOKEN_CODE, msg, null);
}
public static R fail(int errorCode, String msg) {
return new R(errorCode, msg, null);
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
public Object getData() {
return data;
}
public Long getTotal() {
return total;
}
public R setTotal(Long total) {
this.total = total;
return this;
}
}
Constants
/**
* 常量类
*/
public class Constants {
public final static int OK_CODE = 200;
public final static int FAIL_CODE = 400;
public final static int OTHER_FAIL_CODE = 333; // 其它错误
public final static int INVALID_TOKEN_CODE = 10000; // 其它错误
public final static String OK_MSG = "请求成功";
public final static String FAIL_MSG = "请求失败";
public final static int STATUS_0 = 0; // 可用状态
public final static int STATUS_1 = 1; // 禁用状态
}
发送成功之后的展示:
微信官方文档:
https://mp.weixin.qq.com/advanced/tmplmsg?action=faq&token=505847577&lang=zh_CN
总结
wxsb。