java微信开发

原创 2017年04月21日 14:22:12

再一次接入微信开放平台,只是此次以消息为主,不涉及支付,如需看微信支付,请参考另一篇博文。

此篇博文涉及了微信公众号开发的基础功能部分,包括了服务器校验、获取access_token 、创建自定义菜单,接收微信公众平台的消息,并回复消息,主动推送模版消息等,在此基础上,可以按需回复或者推送各类消息。代码注释比较全,所以直接上代码:


首先是controller(实际应用中很多功能入口不是controller,此处是做测试而已),其中/wechat/index的get方法是做服务器验证,post方法是接收微信服务器发送过来的消息


package com.dnkx.controller.wechat;


import com.dnkx.bo.WeChatTextMessageBO;
import com.dnkx.util.WechatUtil;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.SystemUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.Map;

/**
 * Created by 李小拐 on 2017/4/18.
 */
@Controller
@RequestMapping("/wechat")
public class WeChatController {
    private final static Logger logger= LoggerFactory.getLogger(WeChatController.class);
    @Resource
    private HttpServletRequest request;
    @Value("${appId}")
    private String appId;
    @Value("${appSecret}")
    private String appSecret;

    /**
     * 服务器签名验证
     */
    @ResponseBody
    @RequestMapping(value = "/index",method = RequestMethod.GET)
    public String checkSignature(){
        try {
            // 接收微信服务器以Get请求发送的4个参数
            String signature = request.getParameter("signature");
            String timestamp = request.getParameter("timestamp");
            String nonce = request.getParameter("nonce");
            String echoStr = request.getParameter("echostr");

            if (WechatUtil.checkSignature(signature,timestamp,nonce)){
                logger.info("echoStr:{}",echoStr);
                return echoStr;
            }
        }catch (Exception e){
            logger.error(e.getMessage(),e);
        }
        return "";
    }


    /**
     * 接收微信消息
     */
    @ResponseBody
    @RequestMapping(value = "/index",method = RequestMethod.POST,produces = "text/xml; charset=utf-8")
    public String receiveMessage(){
        String message = null;
        try {
            request.setCharacterEncoding("utf-8");
            Map<String, String> map = WechatUtil.xmlToMap(request.getInputStream());
            String toUserName = map.get("ToUserName");//微信公众号
            String fromUserName = map.get("FromUserName");//openid
            String msgType = map.get("MsgType");//接收到消息类型
            String MsgId = map.get("MsgId");//消息id,64位整型

            //消息类型:text(文本消息)、image(图片消息)、voice(语音)、
            // video(视频)、shortvideo(小视频)、location(位置信息)、link(链接)、event(事件)
            switch (msgType){
                case "text":
                    //接收到的消息内容(文本消息特有字段)
                    String content = map.get("Content");

                    break;
                case "event":
                    //事件类型:subscribe(关注)、unsubscribe(取消关注)、SCAN(扫描)、LOCATION(上报位置)、
                    // CLICK(点击自定义菜单拉取消息)、VIEW(点击自定义菜单跳转链接)
                    String event = map.get("Event");
                    switch (event){
                        case "subscribe":
                            //关注后的推送欢迎消息
                            WeChatTextMessageBO bo=new WeChatTextMessageBO();
                            bo.setToUserName(fromUserName);
                            bo.setFromUserName(toUserName);
                            bo.setCreateTime(System.currentTimeMillis());
                            bo.setContent("欢迎关注小妮驿站,我们将竭诚为您服务!");
                            message=WechatUtil.BeanToXml(bo);
                            break;
                        case "unsubscribe":
                            //取消关注后的相关操作
                            logger.info("{}取消了关注",fromUserName);
                            break;
                        case "CLICK":
                            //获取事件的key(可以根据key值做后续操作)
                            String eventKey = map.get("EventKey");

                            break;
                        case "VIEW":
                            //获取事件KEY值,设置的跳转URL
                            String eventKey2 = map.get("EventKey");
                            logger.info("{}点击了view:{}",fromUserName,eventKey2);
                            break;
                        case "TEMPLATESENDJOBFINISH"://模版消息推送状态
                            logger.info("模版消息推送给 {} 的结果:{}",fromUserName,map.get("Status"));
                            break;
                        default:
                            break;
                    }
                    break;
                default:
                    break;
            }
            // 将回应发送给微信服务器
            if (message==null)message="success";
        }catch (Exception e){
            logger.error(e.getMessage(),e);
        }
        return message;
    }

     /**
     * 获取access_token
     */
    @RequestMapping(value = "/getAccessToken",method = RequestMethod.GET)
    public void getAccessToken(){
        WechatUtil.getAccessToken();
    }

    /**
     * 创建自定义菜单
     * @return
     * @throws Exception
     */
    @ResponseBody
    @RequestMapping(value = "/createMenu",method = RequestMethod.GET)
    public String createMenu() throws Exception{
        String redirectUri=URLEncoder.encode("http://test-admin.xxxx.cn/wechat/redirect","utf-8");
        String url="https://open.weixin.qq.com/connect/oauth2/authorize?appid="+appId+"&redirect_uri="+redirectUri+"&response_type=code&scope=snsapi_base&state=dnkx#wechat_redirect";
        String json="{\"button\":[{\"type\":\"view\",\"name\":\"查询快递\",\"url\":\"http://www.xxxx.cn/\"},{\"name\":\"会员中心\",\"sub_button\":[{\"type\":\"view\",\"name\":\"会员福利\",\"url\":\"http://www.xxxx.cn/b2b/h5/vip.html\"},{\"type\":\"view\",\"name\":\"绑定手机\",\"url\":\""+url+"\"}]},{\"name\":\"关于我们\",\"sub_button\":[{\"type\":\"view\",\"name\":\"关于我们\",\"url\":\"http://xxxxx.cn/weixin/h5/aboutUs.html\"},{\"type\":\"view\",\"name\":\"app下载\",\"url\":\"http://a.app.qq.com/o/simple.jsp?pkgname=com.happy.activity\"},{\"type\":\"view\",\"name\":\"公司招聘\",\"url\":\"http://xxxx.cn/weixin/h5/employ.html\"}]}]}";
        if (WechatUtil.createMenu(json)){
            return "create menu success";
        }else {
            return "create menu fail";
        }
    }

    /**
     * 获取网页授权(此处即网页授权第1步里填写回调URL地址)
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/redirect")
    public String redirect(){
        String code = request.getParameter("code");
        if (StringUtils.isBlank(code)){
            return "fail";
        }
        Map map=WechatUtil.getAccessTokenByCode(code);
        if (map!=null&&map.containsKey("access_token")){
            String openId=map.get("openid").toString();//获取到的openId
//            String accessToken=map.get("access_token").toString();//获取到的accessToken
            request.setAttribute("openId",openId);
            return openId;
        }
        return "fail";
    }

    /**
     * 推送模版消息
     */
    @ResponseBody
    @RequestMapping(value = "/sendMessage")
    public String sendMessage(){
        WechatUtil.sendMessage();
        return "finish";
    }
}



然后是主要的工具类:

package com.dnkx.util;

import com.dnkx.common.bo.HttpExecResponse;
import com.dnkx.common.http.HttpClientExecutor;
import com.dnkx.common.utils.StrUtils;
import com.thoughtworks.xstream.XStream;
import org.apache.commons.codec.digest.DigestUtils;

import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ByteArrayEntity;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.StampedLock;


/**
 * Created by 李小拐 on 2017/4/18.
 */
@Component
public class WechatUtil {
    private final static Logger logger= LoggerFactory.getLogger(WechatUtil.class);
    private final static String accessToken="WECHAT_ACCESS_TOKEN";

    @Resource
    private RedisTemplate tokenRedisTemplate;
    @Value("${token}")
    private String token;
    @Value("${appId}")
    private String appId;
    @Value("${appSecret}")
    private String appSecret;

    private static WechatUtil wechatUtil;
    @PostConstruct
    public void init() {
        wechatUtil = this;
        wechatUtil.token=this.token;
        wechatUtil.appId=this.appId;
        wechatUtil.appSecret=this.appSecret;
        wechatUtil.tokenRedisTemplate = this.tokenRedisTemplate;
    }

    /**
     * 签名验证
     * @param signature
     * @param timestamp
     * @param nonce
     * @return
     */
    public static boolean checkSignature(String signature,String timestamp,String nonce){
        String[] arr = new String[] { wechatUtil.token, timestamp, nonce };
        // 排序
        Arrays.sort(arr);
        // 生成字符串
        StringBuilder content = new StringBuilder();
        for (int i = 0; i < arr.length; i++) {
            content.append(arr[i]);
        }
        // sha1加密
        String temp = DigestUtils.sha1Hex(content.toString());
        logger.info("接收到的signature:{}",signature);
        logger.info("参数签名后的signature:{}",temp);
        return temp.equals(signature); // 与微信传递过来的签名进行比较
    }

    /**
     * 获取AccessToken
     * @return
     */
    public static void getAccessToken(){
        try {
            if (!wechatUtil.tokenRedisTemplate.hasKey(accessToken)) {
                String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + wechatUtil.appId + "&secret=" + wechatUtil.appSecret;
                HttpGet httpGet = new HttpGet(url);
                HttpExecResponse executeResult = HttpClientExecutor.execute(httpGet);
                if (null != executeResult && executeResult.isSuccess()) {
                    String result = executeResult.getData();
                    logger.info("get AccessToken result:{}", result);
                    Map map = StrUtils.fromJson(result, Map.class);
                    if (map.containsKey("access_token")) {
                        wechatUtil.tokenRedisTemplate.opsForValue().set(accessToken, map.get("access_token").toString(), 7000, TimeUnit.SECONDS);
                    }
                }
            }
        }catch (Exception e){
            logger.error("getAccessToken error",e);
        }
    }

    /**
     * 创建自定义菜单
     * @param jsonContent json格式字符串,具体请参照开发文档
     * @return
     */
    public static boolean createMenu(String jsonContent){
        if (!wechatUtil.tokenRedisTemplate.hasKey(accessToken)){
            WechatUtil.getAccessToken();
        }
        String url="https://api.weixin.qq.com/cgi-bin/menu/create?access_token="+wechatUtil.tokenRedisTemplate.opsForValue().get(accessToken);
        try {
            HttpPost httpPost = new HttpPost(url);
            httpPost.setEntity(new ByteArrayEntity(jsonContent.getBytes()));
            HttpExecResponse executeResult = HttpClientExecutor.execute(httpPost, 1000, 2000);
            if (null != executeResult && executeResult.isSuccess()) {
                String result = executeResult.getData();
                logger.info("createMenu result:{}",result);
                Map map=StrUtils.fromJson(result,Map.class);
                if (map.get("errmsg").toString().equals("ok")){
                    return true;
                }
            }
        }catch (Exception e){
            logger.error("createMenu error",e);
        }
        return false;
    }

    /**
     * 网页授权:通过code换取网页授权access_token和用户openId
     * @param code
     * @return
     */
    public static Map getAccessTokenByCode(String code){
        String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="+wechatUtil.appId+"&secret="+wechatUtil.appSecret+"&code="+code+"&grant_type=authorization_code";
        HttpGet httpGet = new HttpGet(url);
        HttpExecResponse executeResult = HttpClientExecutor.execute(httpGet);
        if (null != executeResult && executeResult.isSuccess()) {
            String result = executeResult.getData();
            Map map = StrUtils.fromJson(result, Map.class);
            return map;
        }
        return null;
    }

    /**
     * 发送模版消息
     * @return
     */
    public static boolean sendMessage(){
        if (!wechatUtil.tokenRedisTemplate.hasKey(accessToken)){
            WechatUtil.getAccessToken();
        }
        String url="https://api.weixin.qq.com/cgi-bin/message/template/send?access_token="+wechatUtil.tokenRedisTemplate.opsForValue().get(accessToken);
        String content="{\"touser\":\"o94Cev7D_e6vzjZ-qzZfUHAUFst4\",\"template_id\":\"4JkY9NUvF8tXI7fAPfTXUUnCE-C6AD4KhgQqEymqgEI\",\"data\":{\"first\":{\"value\":\"恭喜你购买成功!\",\"color\":\"#173177\"}}}";
        try {
            HttpPost httpPost = new HttpPost(url);
            httpPost.setEntity(new ByteArrayEntity(content.getBytes()));
            HttpExecResponse executeResult = HttpClientExecutor.execute(httpPost, 1000, 2000);
            if (null != executeResult && executeResult.isSuccess()) {
                String result = executeResult.getData();
                Map map=StrUtils.fromJson(result,Map.class);
                if (map.get("errmsg").toString().equals("ok")){
                    return true;
                }
            }
        }catch (Exception e){
            logger.error("sendMessage error",e);
        }
        return false;
    }

    /**
     * xml转map
     * @param ins
     * @return
     * @throws Exception
     */
    public static Map xmlToMap(InputStream ins) throws Exception{
        Map<String, String> map = new HashMap<>();
        SAXReader reader = new SAXReader();            // 使用dom4j解析xml
        Document doc = reader.read(ins);
        Element root = doc.getRootElement();         // 获取根元素
        List<Element> list = root.elements();        // 获取所有节点
        for (Element e : list) {
            map.put(e.getName(), e.getText());
            //logger.info(e.getName() + ":" + e.getText());
        }
        logger.info("{}",map);
        return map;
    }

    /**
     * Bean转xml
     * @param t
     * @param <T>
     * @return
     */
    public static <T> String BeanToXml(T t){
        try {
            XStream xstream = new XStream();
            xstream.alias("xml", t.getClass());
            String xml = xstream.toXML(t);
            return xml;
        }catch (Exception e){
            logger.error(e.getMessage(),e);
            return null;
        }
    }
}

下面是回复文本消息用到的bo对象,如果需要回复其他类型的消息,构建相应bo对象就行了:


package com.dnkx.bo;

/**
 * Created by 李小拐 on 2017/4/20.
 * 回复微信消息
 */
public class WeChatBaseMessageBO {
    private String ToUserName;
    private String FromUserName;
    private Long CreateTime;
    private String MsgType;

    public String getToUserName() {
        return ToUserName;
    }

    public void setToUserName(String toUserName) {
        ToUserName = toUserName;
    }

    public String getFromUserName() {
        return FromUserName;
    }

    public void setFromUserName(String fromUserName) {
        FromUserName = fromUserName;
    }

    public Long getCreateTime() {
        return CreateTime;
    }

    public void setCreateTime(Long createTime) {
        CreateTime = createTime;
    }

    public String getMsgType() {
        return MsgType;
    }

    public void setMsgType(String msgType) {
        MsgType = msgType;
    }
}

package com.dnkx.bo;

/**
 * Created by 李小拐 on 2017/4/20.
 */
public class WeChatTextMessageBO extends WeChatBaseMessageBO{

    public WeChatTextMessageBO(){
        this.setMsgType("text");
    }

    private String Content;

    public String getContent() {
        return Content;
    }

    public void setContent(String content) {
        Content = content;
    }
}

基本功能都在代码里了,只是测试代码,但都亲测通过,基于此代码加上实际需要的业务逻辑就行了(我接下来是基于此做模版消息推送,自动回复,和客服消息)。

最后,仅供参考!仅供参考!仅供参考!


版权声明:本文为博主原创文章,未经博主允许不得转载。

微信小程序java开发流程分享

前段时间,我接触了微信的开发小程序,在开发过程中,我不得不承认微信小程序开发的简单粗暴,不多说,直接hight。 微信小程序可以去公众平台下载。有64位和32位的。在这里我就不多哔哔了。 下载完微...
  • zhongyouda
  • zhongyouda
  • 2017年06月05日 08:54
  • 6300

微信公众平台java开发详解(工程代码+解析)

本次的教程主要是对微信公众平台开发者模式的讲解,网络上很多类似文章,但很多都让初学微信开发的人一头雾水,所以总结自己的微信开发经验,将微信开发的整个过程系统的列出,并对主要代码进行讲解分析,让初学者尽...
  • pamchen
  • pamchen
  • 2014年08月21日 00:51
  • 232534

JAVA微信开发-新手接入指南

JAVA微信开发QQ群:211202664   相信很多人对微信开发已经不那么陌生,我也是从一个微信开发的菜鸟经过各种问题的折磨,然后去搜索引擎搜索各种文章阅读,但是基本都是零散的资料,没有一个统一、...
  • RodJohnsonDoctor
  • RodJohnsonDoctor
  • 2014年03月26日 10:44
  • 79180

详解微信小程序开发

微信应用开放的服务和组件包含如下:  视图容器:视图(View)、滚动视图、Swiper基础内容:图标、文本、进度条表单组件:按钮、表单等等操作反馈导航媒体组建:音频、图片、视频。地图画布文件操...
  • baidu_36418146
  • baidu_36418146
  • 2016年10月19日 14:56
  • 4006

微信小程序开发教程

一、小程序框架概述 以下是腾讯官方对小程序框架的表述: 小程序开发框架的目标是通过尽可能简单、高效的方式让开发者可以在微信中开发具有原生 APP 体验的服务。 框架提供了自己的视图层描述语言 WXML...
  • zhaidp_java
  • zhaidp_java
  • 2016年10月12日 09:26
  • 2255

微信APP支付-Java后台实现

因为自己项目上的APP 原来有支付宝支付,现在想要加上微信支付,所以去研究了微信APP支付的相关技术文档。虽然微信的相关的技术文档已经非常的清楚了。但是我还是想记录一下自己研究过程。 1 注册微信开放...
  • walle167
  • walle167
  • 2016年03月22日 19:01
  • 10997

微信公众开放平台开发08---纯java 实现微信开发:编写自定义菜单

微信公众开放平台开发08---纯java 实现微信开发:编写自定义菜单  微信公众开放平台开发08---纯java 实现微信开发:编写自定义菜单  技术qq交流群:JavaDream:251572...
  • lidew521
  • lidew521
  • 2014年04月08日 23:57
  • 15191

Java微信公众号开发(附源码!!!)

本文中采用原生servlet和jdbc实现微信公众号开发,附源码,源码链接在文章最后。...
  • fanguoddd
  • fanguoddd
  • 2017年02月03日 15:20
  • 20724

微信公众号开发,原来这么简单?[java框架]

可能你不知道,有个框架叫wx-tools(小心翼翼地说) Wx-tools是基于微信公众平台API的轻量级框架。 基于Wx-tools你可以开速开发一个订阅号/服务号的web应用后台。 博主...
  • antgan
  • antgan
  • 2016年12月15日 22:13
  • 39409

Java微信公众平台开发(一)--接入微信公众平台

转载自崔用志博客:http://www.cuiyongzhi.com/ 前面几篇文章一直都在说微信公众平台的开发准备工作,那么从这篇开始我们就将正式的进入JAVA微信公众平台开发的整...
  • Winhye
  • Winhye
  • 2017年11月28日 10:51
  • 1039
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:java微信开发
举报原因:
原因补充:

(最多只允许输入30个字)