1.場景描述:
在小程序開發過程中需要發送消息,由於小程序消息(訂閱消息)需要經常點擊訂閱,用戶操作麻煩,所以將小程序與公眾號進行關聯;
優先發送消息到公眾號。
2.Java代碼實現
主要有以下幾個類
WeChatMessageSendUtil //消息發送類
PubMessage //統一消息發送實體
MpTemplateMsg //公眾號消息模板實體
miniprogram // 跳小程序所需數據,不需跳小程序可不用傳該數據 (注意:路徑前要去掉‘/’ ;如:pages/menu)
STemplateData //公眾號消息模板參數
TemplateModel //訂閱消息模板實體
TemplateData //訂閱消息模板參數 Schedulers //定時器用於維護accessToken SysParam //獲取配置文件參數 WeappTemplateMsg //作廢
2.1.配置文件(application.properties)
#####################################微信小程序參數 for message ################################################# message.wxapp.appid=微信小程序appid message.wxapp.appsecret=微信小程序appsecret
2.2.java Class
import io.swagger.annotations.ApiModelProperty; /** * 跳小程序所需數據,不需跳小程序可不用傳該數據 */ public class miniprogram { @ApiModelProperty(value = "所需跳轉到的小程序appid(該小程序appid必須與發模板消息的公眾號是綁定關聯關系,暫不支持小游戲)", example = "") private String appid; @ApiModelProperty(value = "所需跳轉到小程序的具體頁面路徑,支持帶參數,(示例index?foo=bar),要求該小程序已發布,暫不支持小游戲", example = "") private String page; //private String pagepath; public miniprogram() { } public miniprogram(String appid, String page) { this.appid = appid; this.page = page; } /*public miniprogram(String appid, String pagepath) { this.appid = appid; this.pagepath = pagepath; }*/ public String getAppid() { return appid; } public void setAppid(String appid) { this.appid = appid; } //public String getPagepath() { // return pagepath; //} //public void setPagepath(String pagepath) { // this.pagepath = pagepath; //} public String getPage() { return page; } public void setPage(String page) { this.page = page; } }
import io.swagger.annotations.ApiModelProperty; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import javax.persistence.EntityListeners; import java.util.Map; @EntityListeners(AuditingEntityListener.class) public class MpTemplateMsg { @ApiModelProperty(value = "服務號", example = "") private String appid; @ApiModelProperty(value = "消息模板id", example = "") private String template_id; @ApiModelProperty(value = "模板跳轉鏈接(海外帳號沒有跳轉能力)", example = "") private String url="/index"; @ApiModelProperty(value = "模板參數", example = "") Map<String, STemplateData> data; @ApiModelProperty(value = "跳小程序所需數據,不需跳小程序可不用傳該數據", example = "") private miniprogram miniprogram; public String getAppid() { return appid; } public void setAppid(String appid) { this.appid = appid; } public String getTemplate_id() { return template_id; } public void setTemplate_id(String template_id) { this.template_id = template_id; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public Map<String, STemplateData> getData() { return data; } public void setData(Map<String, STemplateData> data) { this.data = data; } public miniprogram getMiniprogram() { return miniprogram; } public void setMiniprogram(miniprogram miniprogram) { this.miniprogram = miniprogram; } }
import com.redlichee.core.entity.BaseEntity; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import javax.persistence.EntityListeners; import javax.persistence.Id; @EntityListeners(AuditingEntityListener.class) public class PubMessage extends BaseEntity { @Id //收件人openid private String touser; //小程序消息模板 private TemplateModel weapp_template_msg; //服務號消息模板 private MpTemplateMsg mp_template_msg; public PubMessage() { } public PubMessage(String touser, TemplateModel weapp_template_msg, MpTemplateMsg mp_template_msg) { this.touser = touser; this.weapp_template_msg = weapp_template_msg; this.mp_template_msg = mp_template_msg; } public String getTouser() { return touser; } public void setTouser(String touser) { this.touser = touser; } public TemplateModel getWeapp_template_msg() { return weapp_template_msg; } public void setWeapp_template_msg(TemplateModel weapp_template_msg) { this.weapp_template_msg = weapp_template_msg; } public MpTemplateMsg getMp_template_msg() { return mp_template_msg; } public void setMp_template_msg(MpTemplateMsg mp_template_msg) { this.mp_template_msg = mp_template_msg; } }
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.text.SimpleDateFormat; @EnableScheduling @Component @Lazy(false) public class Schedulers { @Autowired SysParam sysParam; private static Logger logger = LoggerFactory.getLogger(Schedulers.class); private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss"); //每隔2秒執行一次 /*@Scheduled(fixedRate = 2000) public void testTasks2() { System.out.println("定時任務執行時間:1111111111111"); } */ /** * access_token 是小程序的全局唯一調用憑據 * access_token 的有效期為 2 個小時,需要定時刷新 access_token,重復獲取會導致之前一次獲取的 * access_token 失效 * 延遲一秒執行 */ @Scheduled(initialDelay = 1000, fixedDelay = 6000*1000 ) public void getAccessToken(){ try { String token = WeChatMessageSendUtil.getAccessToken(sysParam.getWXAPP_APP_ID(),sysParam.getWXAPP_APP_SECRET()); //將獲取到的token放到內存 WeChatMessageSendUtil.ACCESSTOKEN = token; logger.info("獲取到的微信accessToken為"+token); } catch (Exception e) { logger.error("獲取微信accessToken出錯,信息如下"); e.printStackTrace(); } } }
public class STemplateData { private String value;// private String color; public STemplateData() { } public STemplateData(String value, String color) { this.value = value; this.color = color; } public STemplateData(String value) { this.value = value; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } @Override public String toString() { return "TemplateData{" + "value='" + value + '\'' + ", color='" + color + '\'' + '}'; } }
import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component public class SysParam { @Value("${message.wxapp.appid}") private String WXAPP_APP_ID; @Value("${message.wxapp.appsecret}") private String WXAPP_APP_SECRET; public String getWXAPP_APP_ID() { return WXAPP_APP_ID; } public void setWXAPP_APP_ID(String WXAPP_APP_ID) { this.WXAPP_APP_ID = WXAPP_APP_ID; } public String getWXAPP_APP_SECRET() { return WXAPP_APP_SECRET; } public void setWXAPP_APP_SECRET(String WXAPP_APP_SECRET) { this.WXAPP_APP_SECRET = WXAPP_APP_SECRET; } }
public class TemplateData { private String value;// public TemplateData(String value) { this.value = value; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } @Override public String toString() { return "TemplateData{" + "value='" + value + '\'' + '}'; } }
import io.swagger.annotations.ApiModelProperty; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import javax.persistence.EntityListeners; import java.util.Map; /** * @author why * 訂閱消息模板實體 */ @EntityListeners(AuditingEntityListener.class) public class TemplateModel { private String form_id;//收集到的用戶formid private String emphasis_keyword = "keyword1.DATA";//放大那個推送字段 @ApiModelProperty(value = "接收人的openId", example = "") private String touser; @ApiModelProperty(value = "消息模板id", example = "") private String template_id; @ApiModelProperty(value = "跳轉頁", example = "") private String page="/index"; @ApiModelProperty(value = "參數", example = "") Map<String, TemplateData> data; public TemplateModel() { } public TemplateModel(String touser, String template_id, String page, Map<String, TemplateData> data) { this.touser = touser; this.template_id = template_id; this.page = page; this.data = data; } public String getTouser() { return touser; } public void setTouser(String touser) { this.touser = touser; } public String getTemplate_id() { return template_id; } public void setTemplate_id(String template_id) { this.template_id = template_id; } public String getPage() { return page; } public void setPage(String page) { this.page = page; } public Map<String, TemplateData> getData() { return data; } public void setData(Map<String, TemplateData> data) { this.data = data; } @Override public String toString() { return "WxSendModel{" + "touser='" + touser + '\'' + ", template_id='" + template_id + '\'' + ", page='" + page + '\'' + ", data=" + data + '}'; } }
import com.alibaba.fastjson.JSONObject; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.util.EntityUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; import java.util.HashMap; import java.util.Map; @Component public class WeChatMessageSendUtil { public static final String POST_PUB_MESSAGE = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=ACCESS_TOKEN"; public static final String POST_APP_MESSAGE ="https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=ACCESS_TOKEN"; public static String ACCESSTOKEN; @Autowired SysParam sysParam; @Autowired Schedulers schedulers; public static void main(String[] args) { } /** * 統一消息發送模板 */ public Map<String,Object> send(PubMessage pubMessage){ Map<String,Object> resMap=new HashMap<>(); //獲取token if ( ACCESSTOKEN==null){ schedulers.getAccessToken(); } String accessToken =ACCESSTOKEN; //存儲小程序消息模板信息 TemplateModel weapp_template_msg = pubMessage.getWeapp_template_msg(); pubMessage.setWeapp_template_msg(null); RestTemplate restTemplate = new RestTemplate(); //發送服務號消息 ResponseEntity<String> responseEntity = restTemplate.postForEntity(POST_PUB_MESSAGE.replace("ACCESS_TOKEN",accessToken), pubMessage, String.class); String body = responseEntity.getBody(); JSONObject jsonObject = JSONObject.parseObject(body); if ("40001".equals(jsonObject.get("errcode")+"")){//防止AccessToken過期 //AccessToken過期 String token = WeChatMessageSendUtil.getAccessToken(sysParam.getWXAPP_APP_ID(),sysParam.getWXAPP_APP_SECRET()); //將獲取到的token放到內存 WeChatMessageSendUtil.ACCESSTOKEN = token; accessToken=token; responseEntity =restTemplate.postForEntity(POST_PUB_MESSAGE.replace("ACCESS_TOKEN",accessToken), pubMessage, String.class); body = responseEntity.getBody(); jsonObject = JSONObject.parseObject(body); } if ("0".equals(jsonObject.get("errcode")+"")){ //服務號發送成功,不再發送小程消息 resMap.put("code","success"); resMap.put("msg","公眾號消息發送成功"); resMap.put("data",pubMessage); }else{ //服務號發送失敗,發送訂閱消息 String s = WxAppSend(weapp_template_msg, accessToken); JSONObject jsonObject2 = JSONObject.parseObject(s); if ("0".equals(jsonObject2.get("errcode")+"")){ resMap.put("code","success"); resMap.put("msg","小程序消息發送成功"); resMap.put("data",pubMessage); }else{ resMap.put("code","error"); resMap.put("msg","小程序消息發送失敗"); resMap.put("data",jsonObject2); resMap.put("msg2","公眾號消息發送失敗"); resMap.put("data",jsonObject); } } return resMap; } /** * 訂閱消息模板發送 * @param templateModel * @param accessToken * @return */ public static String WxAppSend(TemplateModel templateModel,String accessToken) { String body =""; try { RestTemplate restTemplate = new RestTemplate(); String url = POST_APP_MESSAGE.replace("ACCESS_TOKEN",accessToken); //拼接推送的模版 ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, templateModel, String.class); body = responseEntity.getBody(); }catch (Exception e){ return e.getMessage(); } return body; } /** * 獲取token * @param appId * @param secret * @return */ public static String getAccessToken(String appId,String secret) { Map<String, Object> map = null; String access_token_url = "https://api.weixin.qq.com/cgi-bin/token?" + "grant_type=client_credential" + "&appid="+appId+ "&secret="+secret; try { HttpClient client = HttpClientBuilder.create().build();//構建一個Client HttpGet get = new HttpGet(access_token_url); //構建一個GET請求 HttpResponse response = client.execute(get);//提交GET請求 HttpEntity result = response.getEntity();//拿到返回的HttpResponse的"實體" String content = EntityUtils.toString(result); //System.out.println(content); JSONObject res = JSONObject.parseObject(content); //map = JsonUtil.parseJSON2Map(res); return res.get("access_token")+""; } catch (Exception e) { e.printStackTrace(); System.out.println("獲取信息失敗"); } return "error:access_token"; } }