Java實(shí)現(xiàn)微信公眾號(hào)發(fā)送模版消息
微信公眾號(hào)發(fā)送模版消息 背景:如下圖,當(dāng)用戶發(fā)布需求的時(shí)候,公眾號(hào)自定推送消息。例如:微信支付的時(shí)候,公眾號(hào)會(huì)推送支付成功消息

前提:發(fā)送模版消息,顧名思義,前提就是需要有模版,那么在哪里配置模版呢?
微信公眾號(hào)平臺(tái)–>廣告與服務(wù)–>模版消息–>我的模版
模版消息是已經(jīng)申請(qǐng)過(guò)的模版,如果里面的模版都不符合自己業(yè)務(wù)的話,可以到模版庫(kù)里找,然后添加到「我的模版」。也可以按照自己的需求申請(qǐng)新的模版,一般第二個(gè)工作日會(huì)審核通過(guò)。

在模版詳情可以查看模版的格式,下圖左邊紅框是消息最終展示的效果,
右邊紅框是需要傳的參數(shù)。

有了模版之后,模版ID就是我們要放進(jìn)代碼里的,復(fù)制出來(lái)。
消息模版準(zhǔn)備好之后,暫時(shí)不要寫代碼奧,查看微信開(kāi)發(fā)文檔,看看發(fā)送模版都需要哪些參數(shù)。
微信開(kāi)發(fā)文檔–>基礎(chǔ)消息能力–>模版消息接口–「發(fā)送模版消息」

查看微信開(kāi)發(fā)文檔
發(fā)送模版消息
http請(qǐng)求方式: POST https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN
注:url和miniprogram都是非必填字段,若都不傳則模板無(wú)跳轉(zhuǎn);若都傳,會(huì)優(yōu)先跳轉(zhuǎn)至小程序。開(kāi)發(fā)者可根據(jù)實(shí)際需要選擇其中一種跳轉(zhuǎn)方式即可。當(dāng)用戶的微信客戶端版本不支持跳小程序時(shí),將會(huì)跳轉(zhuǎn)至url。
返回碼說(shuō)明
在調(diào)用模板消息接口后,會(huì)返回JSON數(shù)據(jù)包。
正常時(shí)的返回JSON數(shù)據(jù)包示例:
{
“errcode”:0,
“errmsg”:“ok”,
“msgid”:200228332
}
發(fā)送模版所需參數(shù):
模版ID和openId是必須有的,剩下的就是和自己業(yè)務(wù)有關(guān)了。
上面的內(nèi)容都搞定之后,就可以開(kāi)始擼代碼了
發(fā)送模版微信返回Dto
@Data
public class TemplateMsgResultDto extends ResultState {
/**
* 消息id(發(fā)送模板消息)
*/
private String msgid;
}
發(fā)送模版微信返回狀態(tài)
@Data
public class ResultState implements Serializable {
/**
* 狀態(tài)
*/
private int errcode;
/**
* 信息
*/
private String errmsg;
}
微信模版消息請(qǐng)求參數(shù)實(shí)體類
@Data
public class WxTemplateMsg {
/**
* 接收者openId
*/
private String touser;
/**
* 模板ID
*/
private String template_id;
/**
* 模板跳轉(zhuǎn)鏈接
*/
private String url;
// "miniprogram":{ 未加入
// "appid":"xiaochengxuappid12345",
// "pagepath":"index?foo=bar"
// },
/**
* data數(shù)據(jù)
*/
private TreeMap<String, TreeMap<String, String>> data;
/**
* 參數(shù)
*
* @param value 值
* @param color 顏色 可不填
* @return params
*/
public static TreeMap<String, String> item(String value, String color) {
TreeMap<String, String> params = new TreeMap<String, String>();
params.put("value", value);
params.put("color", color);
return params;
}
}
Java封裝模版信息代碼
public TemplateMsgResultDto noticeTemplate(TemplateMsgVo templateMsgVo) {
// 模版ID
String templateId="XXX";
TreeMap<String, TreeMap<String, String>> params = new TreeMap<>();
//根據(jù)具體模板參數(shù)組裝
params.put("first", WxTemplateMsg.item("恭喜!您的需求已發(fā)布成功", "#000000"));
params.put("keyword1", WxTemplateMsg.item(templateMsgVo.getTaskName(), "#000000"));
params.put("keyword2", WxTemplateMsg.item("需求已發(fā)布", "#000000"));
params.put("remark", WxTemplateMsg.item("請(qǐng)耐心等待審核", "#000000"));
WxTemplateMsg wxTemplateMsg = new WxTemplateMsg();
// 模版ID
wxTemplateMsg.setTemplate_id(templateId);
// openId
wxTemplateMsg.setTouser(templateMsgVo.getOpenId());
// 關(guān)鍵字賦值
wxTemplateMsg.setData(params);
String data = JsonUtils.ObjectToString(wxTemplateMsg);
return handleSendMsgLog(data);
}
發(fā)送模版代碼
private TemplateMsgResultDto handleSendMsgLog(String data) {
TemplateMsgResultDto resultDto = new TemplateMsgResultDto();
try {
resultDto = sendTemplateMsg(data);
} catch (Exception exception) {
log.error("發(fā)送模版失敗", exception);
}
// TODO 可以記錄一下發(fā)送記錄的日志
return resultDto;
}
public TemplateMsgResultDto sendTemplateMsg(String data) throws Exception {
// 獲取token
String accessToken = getAccessToken();
// 發(fā)送消息
HttpResult httpResult = HttpUtils.stringPostJson(ConstantsPath.SEND_MESSAGE_TEMPLATE_URL + accessToken, data);
return IMJsonUtils.getObject(httpResult.getBody(), TemplateMsgResultDto.class);
}
/**
* 獲取全局token
*/
public String getAccessToken() {
String key = ConstantsRedisKey.ADV_WX_ACCESS_TOKEN;
// 從redis緩存中獲取token
if (redisCacheManager.get(key) != null) {
return (String) redisCacheManager.get(key);
}
// 獲取access_token
String url = String.format(ConstantsPath.WX_ACCESS_TOKEN_URL, appid, secret);
ResponseEntity<String> result = restTemplate.getForEntity(url, String.class);
if (result.getStatusCode() == HttpStatus.OK) {
JSONObject jsonObject = JSON.parseObject(result.getBody());
String accessToken = jsonObject.getString("access_token");
// Long expires_in = jsonObject.getLong("expires_in");
redisCacheManager.set(key, accessToken, 1800);
return accessToken;
}
return null;
}
微信地址常量類
public class ConstantsPath {
/**
* 微信公眾號(hào)獲取全局token
*/
public static final String WX_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
/**
* 微信發(fā)送模版消息
*/
public static final String SEND_MESSAGE_TEMPLATE_URL = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=";
}
Json工具類
@Slf4j
public class JsonUtils {
private static ObjectMapper json;
static {
json = new ObjectMapper();
json.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
json.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
/**
* 序列化為JSON字符串
*/
public static String ObjectToString(Object object) {
try {
return (json.writeValueAsString(object));
} catch (Exception e) {
log.error("序列化為JSON字符串出錯(cuò)",e);
}
return null;
}
public static <T> T getObject(String jsonString, Class<T> clazz) {
if (StringUtils.isEmpty(jsonString))
return null;
try {
return json.readValue(jsonString, clazz);
} catch (Exception e) {
log.error("將JSON字符串轉(zhuǎn)化為Map出錯(cuò)",e);
return null;
}
}
}
Http工具類
@Component
@Slf4j
public class HttpUtils {
private static String sourcePath;
public static HttpResult stringPostJson(String path, String content) throws Exception{
return stringPost(path, null, content, "utf-8", "utf-8", "application/json");
}
public static HttpResult stringPost(String path, Map<String,String> headerMap, String content, String contentencode, String encode, String contentType) throws Exception{
StringEntity entity = new StringEntity(content, contentencode);
entity.setContentType(contentType);
return post(path, headerMap, entity, encode);
}
private static HttpResult post(String path, Map<String,String> headerMap, HttpEntity entity, String encode){
HttpResult httpResult = new HttpResult();
CloseableHttpClient httpClient = null;
CloseableHttpResponse response = null;
try{
HttpPost httpPost = new HttpPost(getURI(path));
LaxRedirectStrategy redirectStrategy = new LaxRedirectStrategy();
httpClient = HttpClientBuilder.create().setRedirectStrategy(redirectStrategy).build();
RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(120000)
.setConnectTimeout(120000)
.setConnectionRequestTimeout(120000)
.setCircularRedirectsAllowed(true)
.setRedirectsEnabled(true)
.setMaxRedirects(5)
.build();
httpPost.setConfig(requestConfig);
httpPost.setHeader("User-Agent", header);
if(headerMap != null && headerMap.size() > 0){
for(String name:headerMap.keySet()) {
httpPost.addHeader(name, headerMap.get(name));
}
}
httpPost.setEntity(entity);
response = httpClient.execute(httpPost);
httpResult.setStatus(response.getStatusLine().getStatusCode());
if(httpResult.getStatus() == 200){
HttpEntity resEntity = response.getEntity();
httpResult.setBody(EntityUtils.toString(resEntity, encode));
}
}catch(Exception ex){
log.error("post請(qǐng)求出錯(cuò)", ex);
}finally{
try{
if(response != null){
response.close();
}
if(httpClient != null){
httpClient.close();
}
}catch(Exception ex) {
log.error("post請(qǐng)求關(guān)閉資源出錯(cuò)", ex);
}
}
return httpResult;
}
}
到此這篇關(guān)于Java實(shí)現(xiàn)微信公眾號(hào)發(fā)送模版消息的文章就介紹到這了,更多相關(guān)Java微信公眾號(hào)發(fā)消息內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MyBatis傳入多個(gè)參數(shù)時(shí)parameterType的寫法
這篇文章主要介紹了MyBatis傳入多個(gè)參數(shù)時(shí)parameterType的寫法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12
java substring(a)與substring(a,b)的使用說(shuō)明
這篇文章主要介紹了java substring(a)與substring(a,b)的使用說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-10-10
SpringBoot3結(jié)合gRpc實(shí)現(xiàn)遠(yuǎn)程服務(wù)調(diào)用的流程步驟
gRPC是一個(gè)現(xiàn)代開(kāi)源高性能遠(yuǎn)程過(guò)程調(diào)用(RPC)框架,可以在任何環(huán)境中運(yùn)行,它由Google開(kāi)發(fā),旨在幫助開(kāi)發(fā)人員更輕松地構(gòu)建分布式應(yīng)用,特別是當(dāng)代碼可能在不同地方運(yùn)行的時(shí)候,本文介紹了SpringBoot3結(jié)合gRpc實(shí)現(xiàn)遠(yuǎn)程服務(wù)調(diào)用的流程步驟,需要的朋友可以參考下2024-07-07
Java BeanMap實(shí)現(xiàn)Bean與Map的相互轉(zhuǎn)換
這篇文章主要介紹了利用BeanMap進(jìn)行對(duì)象與Map的相互轉(zhuǎn)換,通過(guò)net.sf.cglib.beans.BeanMap類中的方法來(lái)轉(zhuǎn)換,效率極高,本文給大家分享實(shí)現(xiàn)代碼,感興趣的朋友一起看看吧2022-11-11
SSH框架網(wǎng)上商城項(xiàng)目第22戰(zhàn)之銀行圖標(biāo)以及支付頁(yè)面顯示
Java?Ribbon與openfeign區(qū)別和用法講解
java Comparator.comparing排序使用示例


