java实现微信小程序模板消息推送

最近参与开发的项目有用到微信模板消息推送,在这离记录一下。

1.工具类;用于访问WeChat的API接口;

这里工具类使用了静态内部类,且禁止外部使用new创建对象。

ps:URL这里用final static修饰没问题,但切记不要把微信所需的令牌参数(access_token)也拼接上(之前项目的微信消息推送每次重启项目后一段时间就失效),问题所在就不过多细说了;

package com.icex.frame.utils.wechat;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.icex.bus.sys.entity.WxTempMsgModel;
import com.icex.common.Const;
import com.icex.frame.utils.DateUtil;
import com.icex.frame.utils.HttpClientUtil;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 微信推送模板消息方法
 * 微信工具类
 * @author Jjcc
 * @createTime 2019年07月24日 21:03:00
 */
public class WeChatToolUtil {

    private static final String URL = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=";

    private static Logger logger = LoggerFactory.getLogger(WeChatToolUtil.class.getName());
    /**
     * 构造方法私有化
     * @title WeChatToolUtil
     * @description
     * @author Jjcc
     * @return
     * @createTime
     * @throws
     */
    private WeChatToolUtil() {
        if (null != WeChatUtilInnerClass.WE_CHAT_TOOL) {
            logger.error("实例对象已创建!");
            throw new RuntimeException("实例对象已创建!");
        }
    }

    private static class WeChatUtilInnerClass {
        private final static WeChatToolUtil WE_CHAT_TOOL = new WeChatToolUtil();
    }

    public static WeChatToolUtil getInstance() {
        return WeChatUtilInnerClass.WE_CHAT_TOOL;
    }

    /**
     * @title pushMessage
     * @description
     * @author Jjcc
     * @param openId 微信用户openId
     * @param templateId 模板消息Id
     * @param formId form_id 表单提交场景下,为 submit 事件带上的 formId
     * @param page 点击模板消息后进入的页面
     * @return String
     * @createTime 2019/7/24 21:57
     * @throws
     */
    public String pushMessage(String openId, String templateId, String formId, String page, JSONObject jsonObject) {
    	System.out.println("推送微信消息-----token---------> " + Const.WX_ACCESS_TOKEN);
        WxTempMsgModel model = new WxTempMsgModel();
        model.setAccess_token(Const.WX_ACCESS_TOKEN);
        model.setTouser(openId);
        model.setTemplate_id(templateId);
        model.setForm_id(formId);
        model.setPage(page);

        model.setData(jsonObject);

        String jsonStr = JSON.toJSONString(model);

        String ret;
        try {
            ret = HttpClientUtil.doPost2(URL + Const.WX_ACCESS_TOKEN, jsonStr);
            System.out.println("微信推送模板消息结果ret----------> " + ret);
            JSONObject retObj = JSONObject.parseObject(ret);

            /*
             * 返回状态码
             * 0:正常;
             * 40037:template_id不正确
             * 41028:form_id不正确,或者过期
             * 41029:form_id已被使用
             * 41030:page不正确
             */
            String errCode = retObj.getString("errcode");
            //返回状态消息
            String errMsg = retObj.getString("errmsg");
            logger.info("weChatReturnJson:" + retObj.toJSONString() + ";msg:" + errMsg);
            if (StringUtils.isNotBlank(errCode) && "0".equals(errCode)) {
                // 推送成功
                logger.info("推送成功!");
                return errCode;
            } else {
                logger.error("微信模板消息推送失败!第二次尝试推送");
                //40001  获取access_token时AppSecret错误,或者access_token无效。请开发者认真比对AppSecret的正确性,或查看是否正在为恰当的公众号调用接口
                //42001  access_token超时,请检查access_token的有效期,请参考基础支持-获取access_token中,对access_token的详细机制说明
                if ("40001".equals(errCode) || "42001".equals(errCode)) {
                	//getAccessTokenAndPush(model);
                }
            }
        } catch (Exception e) {
            logger.error("微信模板消息推送异常!");
            e.printStackTrace();
        }
        return "-1";
    }
}

2. 实现WeChat消息模板的功能;

2.1 定义一个微信模板消息的Bean

本人这里并没有直接使用JSONObject组装json

package com.icex.bus.sys.entity;

import java.util.Date;

/**
 * 微信模板消息Bean
 * @author Jjcc
 * @version 1.0.0
 * @Description
 * @ClassName WeChatMessTempPara.java
 * @createTime 2019年07月25日 12:52:00
 */
public class WeChatMessTempPara {

    /**
     * 受理时间
     */
    private Date acceptTime;
    /**
     * 受理人
     */
    private String dealAccountName;
    /**
     * 联系电话
     */
    private String dealAccountPhone;
    /**
     * 工单内容
     */
    private String flowContent;
    /**
     * 工单状态
     */
    private String serverStatusName;
    /**
     * 进度说明
     */
    private String progressExplain;
    /**
     * 处理状态
     */
    private String dealStatus;
    /**
     * 当前进度
     */
    private String presentProgress;
    /**
     * 工作内容
     */
    private String dealContent;

    public Date getAcceptTime() {
        return acceptTime;
    }

    public void setAcceptTime(Date acceptTime) {
        this.acceptTime = acceptTime;
    }

    public String getDealAccountName() {
        return dealAccountName;
    }

    public void setDealAccountName(String dealAccountName) {
        this.dealAccountName = dealAccountName;
    }

    public String getDealAccountPhone() {
        return dealAccountPhone;
    }

    public void setDealAccountPhone(String dealAccountPhone) {
        this.dealAccountPhone = dealAccountPhone;
    }

    public String getFlowContent() {
        return flowContent;
    }

    public void setFlowContent(String flowContent) {
        this.flowContent = flowContent;
    }

    public String getServerStatusName() {
        return serverStatusName;
    }

    public void setServerStatusName(String serverStatusName) {
        this.serverStatusName = serverStatusName;
    }

    public String getProgressExplain() {
        return progressExplain;
    }

    public void setProgressExplain(String progressExplain) {
        this.progressExplain = progressExplain;
    }

    public String getDealStatus() {
        return dealStatus;
    }

    public void setDealStatus(String dealStatus) {
        this.dealStatus = dealStatus;
    }

    public String getPresentProgress() {
        return presentProgress;
    }

    public void setPresentProgress(String presentProgress) {
        this.presentProgress = presentProgress;
    }

    public String getDealContent() {
        return dealContent;
    }

    public void setDealContent(String dealContent) {
        this.dealContent = dealContent;
    }

    @Override
    public String toString() {
        return "WeChatMessTempPara{" +
                "acceptTime=" + acceptTime +
                ", dealAccountName='" + dealAccountName + '\'' +
                ", dealAccountPhone='" + dealAccountPhone + '\'' +
                ", flowContent='" + flowContent + '\'' +
                ", serverStatusName='" + serverStatusName + '\'' +
                ", progressExplain='" + progressExplain + '\'' +
                ", dealStatus='" + dealStatus + '\'' +
                ", presentProgress='" + presentProgress + '\'' +
                ", dealContent='" + dealContent + '\'' +
                '}';
    }
}

2.2 定义一个微信消息模板的接口

package com.icex.frame.utils.wechat.factory;

import com.alibaba.fastjson.JSONObject;
import com.icex.bus.sys.entity.SysFlow;
import com.icex.bus.sys.entity.WeChatMessTempPara;

/**
 * 模板接口
 * @author Jjcc
 * @version 1.0.0
 * @Description
 * @ClassName ITempParam.java
 * @createTime 2019年07月24日 23:57:00
 */
public interface ITempParam {

    /**
     * 组合消息模板方法
     * @title createTempParam
     * @description
     * @author Jjcc
     * @param tempPara
     * @return com.alibaba.fastjson.JSONObject
     * @createTime 2019/9/18 15:45
     * @throws
     */
    JSONObject createTempParam(WeChatMessTempPara tempPara);
}

2.3 定义具体的模板类

具体的模板类实现了克隆;

这里使用了设计模式的思想;面向接口开发,而不是具体的实现类。对扩展开发,对修改关闭;

package com.icex.frame.utils.wechat.factory.impl;

import com.alibaba.fastjson.JSONObject;
import com.icex.bus.sys.entity.TempParamModel;
import com.icex.bus.sys.entity.WeChatMessTempPara;
import com.icex.frame.utils.wechat.factory.ITempParam;

/**
 * 工单处理中消息模板参数
 * @author Jjcc
 * @version 1.0.0
 * @Description
 * @ClassName BeingDisposeTempParamImpl.java
 * @createTime 2019年07月25日 00:05:00
 */
public class BeingDisposeTempParamImpl implements ITempParam,Cloneable {
    @Override
    public JSONObject createTempParam(WeChatMessTempPara tempPara) {
        JSONObject json = new JSONObject();

        json.put("keyword1", new TempParamModel(tempPara.getProgressExplain()));
        json.put("keyword2", new TempParamModel(tempPara.getDealStatus()));
        json.put("keyword3", new TempParamModel(tempPara.getPresentProgress()));
        json.put("keyword4", new TempParamModel(tempPara.getDealContent()));

        return json;
    }

    @Override
    public BeingDisposeTempParamImpl clone() {
        BeingDisposeTempParamImpl tempParam = null;
        try {
            tempParam = (BeingDisposeTempParamImpl) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return tempParam;
    }
}
package com.icex.frame.utils.wechat.factory.impl;

import com.alibaba.fastjson.JSONObject;
import com.icex.bus.sys.entity.TempParamModel;
import com.icex.bus.sys.entity.WeChatMessTempPara;
import com.icex.frame.utils.wechat.factory.ITempParam;

/**
 * 工单处理完成-消息模板参数
 * @author Jjcc
 * @version 1.0.0
 * @Description
 * @ClassName DisposeFinishTempParaImpl.java
 * @createTime 2019年07月25日 00:09:00
 */
public class DisposeFinishTempParaImpl implements ITempParam,Cloneable {
    @Override
    public JSONObject createTempParam(WeChatMessTempPara tempPara) {
        JSONObject json = new JSONObject();
        json.put("keyword1", new TempParamModel(tempPara.getDealStatus()));
        System.out.println("tempPara.getServerStatusName(): " + tempPara.getServerStatusName());
        json.put("keyword2", new TempParamModel(tempPara.getDealAccountPhone()));
        json.put("keyword3", new TempParamModel(tempPara.getDealAccountName()));

        return json;
    }

    @Override
    public DisposeFinishTempParaImpl clone() {
        DisposeFinishTempParaImpl tempParam = null;
        try {
            tempParam = (DisposeFinishTempParaImpl) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return tempParam;
    }
}

2.4 定义一个用于创建具体的消息模板对象的工厂

该工厂使用的是静态工厂模式,通过枚举类型来决定创建具体的消息模板对象,同时也使用了克隆模式来优化频繁的创建对象;

package com.icex.frame.utils.wechat.factory;

import com.icex.frame.utils.wechat.factory.impl.BeingDisposeTempParamImpl;
import com.icex.frame.utils.wechat.factory.impl.DisposeFinishTempParaImpl;
import com.icex.frame.utils.wechat.factory.impl.OrderReceivingTempParamImpl;

import javax.validation.constraints.NotNull;

/**
 * 微信模板消息工厂
 * @author Jjcc
 * @version 1.0.0
 * @Description
 * @ClassName WeChatTempMsgFactory.java
 * @createTime 2019年07月26日 10:50:00
 */
public class WeChatTempMsgFactory {

    private static OrderReceivingTempParamImpl orderReceivingTempParam;
    private static BeingDisposeTempParamImpl beingDisposeTempParam;
    private static DisposeFinishTempParaImpl disposeFinishTempPara;

    /**
     * 静态工厂方法,通过参数返回指定的类实例对象
     * @title getTempMsgIns
     * @description
     * @author Jjcc
     * @param type
     * @return com.icex.frame.utils.wechat.factory.ITempParam
     * @createTime 2019/7/26 14:36
     * @throws
     */
    public static ITempParam getTempMsgIns(@NotNull WeChatTempMsgType type) {
        ITempParam tempParam = null;
        switch (type) {
            case ORDER_RECEIVING:
                if (null == orderReceivingTempParam) {
                    orderReceivingTempParam = new OrderReceivingTempParamImpl();
                    tempParam = orderReceivingTempParam;
                } else {
                    tempParam = orderReceivingTempParam.clone();
                }
                break;
            case BEING_DISPOSE:
                if (null == beingDisposeTempParam) {
                    beingDisposeTempParam = new BeingDisposeTempParamImpl();
                    tempParam = beingDisposeTempParam;
                } else {
                    tempParam = beingDisposeTempParam.clone();
                }
                break;
            case DISPOSE_FINISH:
                if (null == disposeFinishTempPara) {
                    disposeFinishTempPara = new DisposeFinishTempParaImpl();
                    tempParam = disposeFinishTempPara;
                } else {
                    tempParam = disposeFinishTempPara.clone();
                }
                break;
            default:
        }
        return tempParam;
    }
}

2.5 枚举类

用于限制工厂方法创建对象所传入的参数类型;

package com.icex.frame.utils.wechat.factory;

/**
 * 选择微信模板消息参数
 * @author Jjcc
 * @version 1.0.0
 * @Description
 * @ClassName WeChatTempMsgType.java
 * @createTime 2019年07月26日 14:45:00
 */
public enum WeChatTempMsgType {

    /**
     * 工单受理-模板消息参数
     */
    ORDER_RECEIVING,
    /**
     * 工单处理中消息模板参数
     */
    BEING_DISPOSE,
    /**
     * 工单处理完成-消息模板参数
     */
    DISPOSE_FINISH,
}

3. WeChat令牌刷新功能

微信的accept_token存活时间只有7200S,超过7200S该令牌就会失效,必须在令牌有效期内访问Wechat的API重新刷新accpet_token;我的解决思路是使用Spring中的定时任务组件import org.quartz.Job来定时请求WeChat提供的API来刷新令牌存活时间;

关于quartz这里不细说,配置quartz时,设置为项目启动成功后便执行定时任务;




	
	
	
	
		
			
				
				
			
		
		
		  
		
	
	


	
	
	
		
	      
	    
	
	
		
		
	
	

	
	
	
		
		
	
	
		
		
	
	
package com.icex.bus.job;

import org.apache.commons.lang3.StringUtils;
import org.quartz.Job;
import org.quartz.JobExecutionContext;

import com.alibaba.fastjson.JSONObject;
import com.icex.common.Const;
import com.icex.frame.utils.DateUtil;
import com.icex.frame.utils.HttpClientUtil;

/**
 * 刷新微信accessToken;
 * accessToken的存活时间:7200Second;
 * 定时任务3200Second执行一次刷新accessToken存活时间;
 * 服务器重启后自动执行;
 * @description
 * @author Jjcc
 * @createTime 2019/7/24 20:55
 */
public class WxAccessTokenJob extends BaseJob implements Job {
	
	private static final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + Const.WX_APP_ID + "&secret=" + Const.WX_APP_SECRET;

	@Override
	public void execute(JobExecutionContext arg0) {
		System.out.println("------------获取微信token--------------");
		
		try {
			int i = 1;
			while(i < 11) {
				i++;
				String ret = HttpClientUtil.doGet(ACCESS_TOKEN_URL, null);
				JSONObject json = JSONObject.parseObject(ret);
				String errcode = json.getString("errcode");
				if ("0".equals(errcode) || StringUtils.isBlank(errcode)) {
					//请求成功
					Const.WX_ACCESS_TOKEN = json.getString("access_token");
					System.out.println("时间:" + DateUtil.getCurrentTime() + "刷新token成功;token为---> " + Const.WX_ACCESS_TOKEN);
					break;
				}
			}
			
			System.out.println("本次刷新token使用次数为:---> " + (i-1));
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}

4. formeId问题

小程序中,生成的formId只能使用一次,且每个formeId的生命期只有7day,创建时间超过7day的formeId无法成功使用;

这里本人写了一个定时任务,每天的凌晨12点会删除存储formId表中超过7day的formId;具体的代码就不贴了,定时任务的配置同令牌刷新一起贴出了;

5. 实例

该方法通过openId找到对应formId,并执行工具类中封装的方法,这里不管推送是否成功,都会删除拿到的formId;

/**
     * 微信模板消息推送
     * @title WeChatMsgPush
     * @description
     * @author Jjcc
     * @param openId
     * @param informTempId 微信消息模板Id
     * @param page 跳转的页面
     * @param weChatMessTempPara 模板消息参数对象
     * @param msgTempParaType 模板类型
     * @return void
     * @createTime 2019/7/26 12:01
     * @throws
     */
    public void weChatMsgPush(String openId, String informTempId, String page, WeChatMessTempPara weChatMessTempPara, WeChatTempMsgType msgTempParaType, ISysFormidService formidService) {
        try {
            SysFormid sysFormid = new SysFormid();
            sysFormid.setOpenId(openId);
            List sysFormidList = formidService.select(sysFormid);

            if (0 != sysFormidList.size()) {
                String formId = sysFormidList.get(0).getFormId();

                if (StringUtils.isNotBlank(openId) && StringUtils.isNotBlank(formId)) {
                    WeChatToolUtil toolUtil = WeChatToolUtil.getInstance();
                    ITempParam tempMsgIns = WeChatTempMsgFactory.getTempMsgIns(msgTempParaType);

                    JSONObject jsonObject = tempMsgIns.createTempParam(weChatMessTempPara);

                    String resultStr = toolUtil.pushMessage(openId, informTempId, formId, page, jsonObject);

                    //删除formId
                    Example example = new Example(SysFormid.class);
                    example.createCriteria().andEqualTo("formId", formId);
                    int i = formidService.deleteByExample(example);
                    logger.error("微信模板消息推送结果:" + resultStr);
                    logger.error("删除formId结果:" + i);

                } else {
                    logger.error("微信模板消息推送失败------->formId或openId参数为空!");
                }
            } else {
                logger.error("当前openId无可用的formId");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

使用方式

模板参数实体bean
WeChatMessTempPara weChatMessTempPara = new WeChatMessTempPara();
weChatMessTempPara.setAcceptTime(now);
weChatMessTempPara.setServerStatusName("已受理");
weChatMessTempPara.setFlowContent(obj.getFlowContent());

/**
 * openId:微信账号唯一标识符;
 * ACCEPT_INFORM:微信的消息模板Id;
 * PAGES:点击推送的消息后跳转至小程序中的页面;
 * weChatMessTempPara:模板参数实体bean
 * WeChatTempMsgType.ORDER_RECEIVING:选择何种模板
 * sysFormidService:用于获取openId拥有的formId的对象
 */
weChatMsgPush(openId, ACCEPT_INFORM, PAGES + id, weChatMessTempPara, WeChatTempMsgType.ORDER_RECEIVING, sysFormidService);

end!!!

你可能感兴趣的:(java,WeChat,push)