1. 程式人生 > >微信公眾號模板推送

微信公眾號模板推送

公眾號推送訊息是十分重要又基礎的功能,這裡主要記錄一下微信公眾號推送模板開發的相關流程和程式碼。

首先:要申請模板訊息功能,選擇主營行業和副營行業,並輸入申請理由(相應的例子網上一大堆,這裡不贅述了);

申請完了之後就感覺你選擇的行業去模板庫裡面找類似需要的模板型別,模板庫其實還是蠻全的,實在沒有找到也可以自己申請新的模板,不過每個月有上限;

先上程式碼:

先選擇一個模板(這裡放一個簡單的模板):

{{first.DATA}}
退款原因:{{reason.DATA}}
退款金額:{{refund.DATA}}
{{remark.DATA}}

原理就是按照模板的資料格式往裡面傳值,具體情況看程式碼

結果是這樣的:

controller:

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import com.edgecdj.domain.JsonResult;
import com.edgecdj.wxpublic.service.WxInfoService;
import com.edgecdj.wxpublic.utils.wxtemplate.ParkTemplate;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;

/**
 * 微信公眾號相關介面
 * 
 * @author cdj
 *
 */
@Api("微信公眾號相關介面")
@RestController("/WxPublic")
public class WxPublicController {
	private Log log = LogFactory.getLog(WxPublicController.class);
	@Autowired
	ParkTemplate parkTemplate;
	
	
	@ApiOperation("微信傳送模板資訊")
	@ApiImplicitParams({
			@ApiImplicitParam(paramType = "query", name = "openId", value = "openId", required = true, dataType = "String")
     })
	@PostMapping("/parkTemplateTest")
	public JsonResult parkTemplateTest(String openId) {
		try {
			parkTemplate.parkRefundTemplate(openId, "reason--退款原因", "refund--退款金額");
			return JsonResult.success("傳送成功");
		} catch (Exception e) {
			// TODO: handle exception
			log.error(e.getMessage());
			return JsonResult.failMsg(e.getMessage());
		}
	}
}

建立實體類:

WechatTemplate:

package com.edgecdj.wxpublic.entity.wechattemplate;

import java.util.Map;

/**
 * 微信訊息模板實體類
 * @author cdj
 */
public class WechatTemplate {
	private String touser;
	 
    private String template_id;
 
    private String url;
 
    private Map<String, TemplateData> 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 getUrl() {
        return url;
    }
 
    public void setUrl(String url) {
        this.url = url;
    }
 
    public Map<String, TemplateData> getData() {
        return data;
    }
 
    public void setData(Map<String, TemplateData> data) {
        this.data = data;
    }

}

TemplateData:

package com.edgecdj.wxpublic.entity.wechattemplate;

/**
 * 模板資料
 * 
 * @author cdj
 */
public class TemplateData {
	private String value;
	private String color;

	public String getValue() {
		return value;
	}

	public void setValue(String value) {
		this.value = value;
	}

	public String getColor() {
		return color;
	}

	public void setColor(String color) {
		this.color = color;
	}

}

新建工具類:

ParkTemplateMap:(key一定要和模板的可以一 一對應)

import com.edgecdj.wxpublic.entity.wechattemplate.TemplateData;


/**
 * 訊息模板獲取
 * 
 * @author cdj
 * @date 2018年7月30日 上午8:38:04
 */
public class ParkTemplateMap {

	/**
	 * 退款通知
	 {{first.DATA}}
	退款原因:{{reason.DATA}}
	退款金額:{{refund.DATA}}
	{{remark.DATA}}
	 */
	public static HashMap<String, TemplateData> getParkRefundTemplateMap(String title, String reason, String refund,
			String remarkstr) {
		HashMap<String, TemplateData> mapdata = new HashMap<>();
		// 封裝模板資料
		TemplateData first = new TemplateData();
		first.setValue(title);
		first.setColor("#173177");
		mapdata.put("first", first);
		TemplateData keyword1 = new TemplateData();
		keyword1.setValue(reason);
		keyword1.setColor("#173177");
		mapdata.put("reason", keyword1);
		TemplateData keyword2 = new TemplateData();
		keyword2.setValue(refund);
		keyword2.setColor("#173177");
		mapdata.put("refund", keyword2);
		TemplateData remark = new TemplateData();
		remark.setValue(remarkstr);
		remark.setColor("#173177");
		mapdata.put("remark", remark);
		return mapdata;
	}
}

ParkTemplate:

import java.util.HashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.edgecdj.constant.WxPublicProperties;
import com.edgecdj.paydemo.wxpay.utils.http.HttpsUtil;
import com.edgecdj.wxpublic.entity.WxToken;
import com.edgecdj.wxpublic.entity.wechattemplate.TemplateData;
import com.edgecdj.wxpublic.entity.wechattemplate.WechatTemplate;
import com.edgecdj.wxpublic.utils.UserInfoUtil;
import com.edgecdj.wxpublic.utils.WxTemplateProperties;

/**
 * 微信公眾號車輛進出通知模板
 * 
 * @author cdj
 */
@Component
public class ParkTemplate {

	private Logger logger = LoggerFactory.getLogger(getClass());

	/**
	 * 停車退款通知
	 * 
	 * @param openId  使用者openid
	 * @param reason  退款原因
	 * @param refund  退款金額
	 */
	public void parkRefundTemplate(String openId, String reason, String refund) {
		String jssdkAcceTokenUrl = UserInfoUtil.getJssdkAcceTokenUrl(WxPublicProperties.APPID,
				WxPublicProperties.APPSCREAT);
		String accesstoken = HttpsUtil.httpsRequestToString(jssdkAcceTokenUrl, "GET", null);
		WxToken accToken = JSONObject.parseObject(accesstoken, WxToken.class);
		String templateSendUrl = UserInfoUtil.getTemplateSendUrl(accToken.getAccessToken());
		WechatTemplate wechatTemplate = new WechatTemplate();
		wechatTemplate.setTemplate_id(WxTemplateProperties.TEMPLATE_ID);
		wechatTemplate.setTouser(openId);
        //設定url的話會點選模板訊息會跳到指定的url
		wechatTemplate.setUrl(WxTemplateProperties.REFUND_URL);
		HashMap<String, TemplateData> mapdata = new HashMap<>();
		String title = "停車退款訊息提醒";
		String remark = "如有疑問,請聯絡客服。";
		// 封裝模板資料
		mapdata = ParkTemplateMap.getParkRefundTemplateMap(title, reason, refund, remark);
		wechatTemplate.setData(mapdata);
		logger.info(JSON.toJSONString(wechatTemplate));
		String resultJson = HttpsUtil.httpsRequestToString(templateSendUrl, "POST", JSON.toJSONString(wechatTemplate));
		JSONObject resultJsonObject = JSONObject.parseObject(resultJson);
		logger.info(JSON.toJSONString(resultJsonObject));
		int result = 0;
		if (null != resultJsonObject) {
			if (0 != resultJsonObject.getIntValue("errcode")) {
				result = resultJsonObject.getIntValue("errcode");
				logger.error("錯誤 errcode:{} errmsg:{}", resultJsonObject.getIntValue("errcode"),
						resultJsonObject.get("errmsg").toString());
			}
		}
	}
}

--------------------------------------------------------  手動分割 --------------------------------------------------------

還有幾個方法前面我有發過的,這次再發一次好了

UserInfoUtil:

package com.edgecdj.wxpublic.utils;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Formatter;
import java.util.HashMap;
import java.util.UUID;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class UserInfoUtil {

	private Logger logger = LoggerFactory.getLogger(getClass());

	// 1.獲取code的請求地址
	public static String Get_Code = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=STAT#wechat_redirect";

	// 替換字串
	public static String getCode(String APPID, String REDIRECT_URI, String SCOPE) {
		return String.format(Get_Code, APPID, REDIRECT_URI, SCOPE);
	}

	// 2.獲取Web_access_tokenhttps的請求地址
	public static String Web_access_tokenhttps = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code";

	// 替換字串
	public static String getWebAccess(String APPID, String SECRET, String CODE) {
		return String.format(Web_access_tokenhttps, APPID, SECRET, CODE);
	}

	// 3.拉取使用者資訊的請求地址
	public static String User_Message = "https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN";

	// 替換字串
	public static String getUserMessage(String access_token, String openid) {
		return String.format(User_Message, access_token, openid);
	}

	// 微信JSSDK的AccessToken請求URL地址(全域性access_token)
	public final static String weixin_jssdk_acceToken_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
	// 微信JSSDK的ticket請求URL地址
	public final static String weixin_jssdk_ticket_url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=%s&type=jsapi";
	// 微信傳送訊息模板請求的url地址
	public final static String template_send_url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=%s";
	
//	public final static String weixin_jssdk_ticket_url = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token=%s";

	// 微信公眾號素材列表獲取
    public final static String MATERIAL_URL = "https://api.weixin.qq.com/cgi-bin/material/batchget_material?access_token=%s";
    
    // 微信公眾號素材列表獲取
    public final static String GETMATERIAL_BYID_URL = "https://api.weixin.qq.com/cgi-bin/media/get?access_token=%s&media_id=%s";

    // 微信公眾號建立選單按鈕
    public final static String CREATEBUTTON_URL = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=%s";
    
    /**
     * 獲取微信公眾建立選單的url
     */
    public static String getCreateButton_Url(String accessToken) {
    	return String.format(CREATEBUTTON_URL, accessToken);
    }
    /**
	 *  MATERIAL_URL獲取(素材列表)
	 */
	public static String getMaterial_Url(String accessToken) {
		return String.format(MATERIAL_URL, accessToken);
	}
	public static String getMaterialById_Url(String accessToken, String media_id) {
		return String.format(GETMATERIAL_BYID_URL, accessToken, media_id);
	}
    
	/**
	 *  JSSDK AccessToken 獲取(全域性access_token)
	 */
	public static String getJssdkAcceTokenUrl(String appid, String secret) {
		return String.format(weixin_jssdk_acceToken_url, appid, secret);
	}

	/**
	 * JSSDK ticket url 獲取
	 */
	public static String getJsSdkTicketUrl(String access_token) {
		return String.format(weixin_jssdk_ticket_url, access_token);
	}
	/**
	 * 微信傳送訊息模板請求的url地址 獲取
	 */
	public static String getTemplateSendUrl(String access_token) {
		return String.format(template_send_url, access_token);
	}

	/** JSSDK 獲取簽名等引數 */
	public static HashMap<String, String> sign(String jsapi_ticket, String url) {
		HashMap<String, String> ret = new HashMap<String, String>();
		String nonce_str = create_nonce_str();
		String timestamp = create_timestamp();
		String string1;
		String signature = "";

		// 注意這裡引數名必須全部小寫,且必須有序
		string1 = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + nonce_str + "&timestamp=" + timestamp + "&url=" + url;
		System.out.println(string1);

		try {
//			signature = new SHA1().getDigestOfString(string1.getBytes("utf-8"));
			MessageDigest crypt = MessageDigest.getInstance("SHA-1");
			crypt.reset();
			crypt.update(string1.getBytes("UTF-8"));
			signature = byteToHex(crypt.digest());
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}	

		ret.put("url", url);
		ret.put("jsapi_ticket", jsapi_ticket);
		ret.put("nonceStr", nonce_str);
		ret.put("timestamp", timestamp);
		ret.put("signature", signature);

		return ret;
	}

	private static String byteToHex(final byte[] hash) {
		Formatter formatter = new Formatter();
		for (byte b : hash) {
			formatter.format("%02x", b);
		}
		String result = formatter.toString();
		formatter.close();
		return result;
	}

	private static String create_nonce_str() {
		return UUID.randomUUID().toString();
	}

	private static String create_timestamp() {
		return Long.toString(System.currentTimeMillis() / 1000);
	}

}

HttpsUtil:

package com.edgecdj.paydemo.wxpay.utils.http;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;

import java.io.*;
import java.net.URL;


public class HttpsUtil {

    /**
     * 以https方式傳送請求並將請求響應內容以String方式返回
     *
     * @param path   請求路徑
     * @param method 請求方法
     * @param body   請求資料體
     * @return 請求響應內容轉換成字串資訊
     */
    public static String httpsRequestToString(String path, String method, String body) {
        if (path == null || method == null) {
            return null;
        }

        String response = null;
        InputStream inputStream = null;
        InputStreamReader inputStreamReader = null;
        BufferedReader bufferedReader = null;
        HttpsURLConnection conn = null;
        try {
            // 建立SSLConrext物件,並使用我們指定的信任管理器初始化
            TrustManager[] tm = {new JEEWeiXinX509TrustManager()};
            SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
            sslContext.init(null, tm, new java.security.SecureRandom());

            // 從上述物件中的到SSLSocketFactory
            SSLSocketFactory ssf = sslContext.getSocketFactory();

            System.out.println(path);

            URL url = new URL(path);
            conn = (HttpsURLConnection) url.openConnection();
            conn.setSSLSocketFactory(ssf);

            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);

            //設定請求方式(git|post)
            conn.setRequestMethod(method);

            //有資料提交時
            if (null != body) {
                OutputStream outputStream = conn.getOutputStream();
                outputStream.write(body.getBytes("UTF-8"));
                outputStream.close();
            }

            // 將返回的輸入流轉換成字串
            inputStream = conn.getInputStream();
            inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
            bufferedReader = new BufferedReader(inputStreamReader);
            String str = null;
            StringBuffer buffer = new StringBuffer();
            while ((str = bufferedReader.readLine()) != null) {
                buffer.append(str);
            }

            response = buffer.toString();
        } catch (Exception e) {

        } finally {
            if (conn != null) {
                conn.disconnect();
            }
            try {
                bufferedReader.close();
                inputStreamReader.close();
                inputStream.close();
            } catch (IOException execption) {

            }
        }
        return response;
    }
    
    
    /**
     * GET 請求 獲得圖片反饋
     */
    public static byte[] getFileStream(String url){
        HttpClient client = HttpClientBuilder.create().build();
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(3000).setConnectTimeout(10000).build();
        HttpGet request = new HttpGet(url);
        request.setConfig(requestConfig);
        byte[] res  = null;
        try {
            HttpResponse response = client.execute(request);
            byte[] fileStream = EntityUtils.toByteArray(response.getEntity());
            return fileStream;
        }catch (Exception ex){
        }
        return res;
    }
}

應該就這些了,結果是這樣的