2017-09-23 07:31:24 towtotow 阅读数 896
  • 微信开发核心基础

    很多公司,企业甚至政府部分都开通了微信公众号,想要发挥微信的大价值,就必须让微信跟自己的平台或者数据库联系起来。这套《微信开发核心基础》,用一个完整的案例,告诉你微信开发流程,错误调试,如何看开发文档,学习你就可以自己做些独立开发。这个教程使用PHP为开发工具,建议你先学习子恒老师的《微信公众号使用》。学习完后,可以跟我反馈,如果好的话,会推出《微信服务号开发》教程,QQ/微信:68183131

    13127 人正在学习 去看看 秦子恒

虽然子恒老师已经出品了100多部微信公众号开发的视频教程,

但也不可能把微信所有的api开发接口讲完,

因为微信每过一段时间,

就会推出新的api接口,

你可以实现更多新功能,

还有可能微信把一些接口进行修改或者取消。


13kanwendang.jpg


作为一个优秀的微信开发人员,

你必须学会看微信的开发文档。


这一点对大部分刚接触公众号开发的人来说,

感觉到很难,

因为开发环境的原因,

或者文档说明不够详细,


子恒老师在这一节微信开发视频教程中告诉你怎么看公众号开发文档…

怎么看微信公众号开发文档视频教程在线观看

http://edu.csdn.net/course/detail/2586/40531

2017-09-23 07:27:19 towtotow 阅读数 2730
  • 微信开发核心基础

    很多公司,企业甚至政府部分都开通了微信公众号,想要发挥微信的大价值,就必须让微信跟自己的平台或者数据库联系起来。这套《微信开发核心基础》,用一个完整的案例,告诉你微信开发流程,错误调试,如何看开发文档,学习你就可以自己做些独立开发。这个教程使用PHP为开发工具,建议你先学习子恒老师的《微信公众号使用》。学习完后,可以跟我反馈,如果好的话,会推出《微信服务号开发》教程,QQ/微信:68183131

    13127 人正在学习 去看看 秦子恒

我们在《玩转微信公众号》的视频中,

已经知道公众号分为订阅号,服务号,应用号,

微信公众号开发时,

每个公众号类型会有不同的权限。


12api_quanxian.jpg


比如在开发微信支付功能时,

只有微信服务号能够拥有这个功能,

订阅号就不能使用。


当然还有很多区别,

在这一节微信开发视频教程中告诉你如何查看…

微信公众号开发接口权限与次数限制视频教程在线观看

http://edu.csdn.net/course/detail/2586/40532

2019-12-24 11:26:41 csdn_fpf 阅读数 171
  • 微信开发核心基础

    很多公司,企业甚至政府部分都开通了微信公众号,想要发挥微信的大价值,就必须让微信跟自己的平台或者数据库联系起来。这套《微信开发核心基础》,用一个完整的案例,告诉你微信开发流程,错误调试,如何看开发文档,学习你就可以自己做些独立开发。这个教程使用PHP为开发工具,建议你先学习子恒老师的《微信公众号使用》。学习完后,可以跟我反馈,如果好的话,会推出《微信服务号开发》教程,QQ/微信:68183131

    13127 人正在学习 去看看 秦子恒

说明:

       微信公众号开发只作参考,不作限制,条条大路通罗马,本文只是实现当前业务的一种方式,需要优化的地方还有很多,共勉。

       应系统业务拓展,加入微信公众号模块。微信公众号开发流程如下,本文共分六个部分:

       1)微信后台与项目对接;(开启开发者模式)

       2)获取AccessToken;

       3)自定义菜单;

       4)获取用户openid(用户在微信注册的基本信息)

       5)页面跳转控制;

       6)接收事件推送

一、微信后台与项目对接

       通过阅读官方文档,可以得知若要接入微信公众号平台开发,开发者需要按照以下三步流程:

       1、填写服务器配置

        

        解释:

               1)服务器地址(URL):域名指向的IP端口必须是80端口和443端口

               2)令牌(Token):须与后台配置的一致

               3)消息加解密密钥(EncodingAESKey):随机生成即可

               4)消息加解密方式:我选择的是明文模式,选加密模式也可以,看项目的私密性而定

               5)最重要的一点:配服务器信息之后,需要添加白名单(域名指向的IP)

        2、验证服务器地址的有效性

             点击上图的提交,验证服务器地址等相关信息的有效性

             验证通过后选择启用,则进入开发者模式,通过微信后台自定义事件失去效果

             注意:

                    我们的应用服务器要接受微信服务器的get请求,其中包括四个参数(signature、timestamp、nonce、echostr),开                 发者通过检验signature对请求进行校验。若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生               效,成为开发者成功,否则接入失败。

             1) 排序:将token、timestamp、nonce三个参数进行字典序排序

             2) 加密:将三个参数字符串拼接成一个字符串进行SHA1加密,开发者获得加密后的字符串可与signature对比,标识该请求来源于微信

    // 此处TOKEN即我们刚刚所填的token,须与微信后台配置的token保持一致
    private static final String TOKEN = "obdkurongshenji";


    @GetMapping(value = "/login")
    public String checkName(@RequestParam(name = "signature") String signature,
            @RequestParam(name = "timestamp") String timestamp, @RequestParam(name = "nonce") String nonce,
            @RequestParam(name = "echostr") String echostr) throws UnsupportedEncodingException {
        // 排序
        String sortString = sort(TOKEN, timestamp, nonce);
        // 加密
        String myString = sha1(sortString);
        // 校验
        if (myString != null && myString != "" && myString.equals(signature)) {
            // 如果检验成功原样返回echostr,微信服务器接收到此输出,才会确认检验完成。
            logger.debug("-----------------------签名校验通过------------------------");
            // 首次接入打开,成功创建之后请注释掉(生成自定义菜单)
            Long id = tbWxAccesstokenService.selectMaxId();// 获取最后一个(未过期)token
            String access_token = tbWxAccesstokenService.selectAccessTokeById(id);
            logger.debug("-----------后端服务器地址:" + backUrl);
            if (null != access_token) {
                Menu menu = WebchatMenuUtil.initMenu(backUrl, appId);
                WebchatMenuUtil.createMenu(access_token, JSON.toJSONString(menu));
            } else {
                logger.debug("-----------access_token获取失败------------");
            }
            return echostr;
        } else {
            logger.debug("-----------------------签名校验失败------------------------");
            return null;
        }
    }

       解释:

              代码中涉及到的排序,加密的代码示例如下:  
    // 排序方法
    public String sort(String token, String timestamp, String nonce) {
        String[] strArray = { token, timestamp, nonce };
        Arrays.sort(strArray);
        StringBuilder sb = new StringBuilder();
        for (String str : strArray) {
            sb.append(str);
        }
        return sb.toString();
    }

    /**
     * 将字符串进行sha1加密
     * @param str 需要加密的字符串
     * @return 加密后的内容
     */
    public String sha1(String str) {
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-1");
            digest.update(str.getBytes());
            byte messageDigest[] = digest.digest();
            // Create Hex String
            StringBuffer hexString = new StringBuffer();
            // 字节数组转换为 十六进制 数
            for (int i = 0; i < messageDigest.length; i++) {
                String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
                if (shaHex.length() < 2) {
                    hexString.append(0);
                }
                hexString.append(shaHex);
            }
            return hexString.toString();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }

二、获取AccessToken

       1、定义:access_token是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。

       2、在库融审计项目中,用的是access_token持久化,每隔一个小时获取到新的access_token,则保存到数据库中,调用微信接口需要access_token时,从数据库中取出最新的access_token即可。

       3、接口请求方式:GET

       4、接口:https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET

       5、接口详情:

            

三、自定义菜单

        1、自定义菜单能够帮助公众号丰富界面,让用户更好更快的理解公众号的功能。开启自定义菜单后,公众号界面如图所示:

              

注意:

       1)自定义菜单最多包含3个一级菜单,每个菜单最多包含5个二级菜单

       2)一级菜单最多四个汉字,二级菜单最多七个汉字,多出来的部分将会以”…”代替

       3)创建自定义菜单后,菜单的刷新策略是,在用户进入公众号会话页或公众号profile页时,如果发现上一次拉取菜单的请求在5分钟以前,就会拉取一下菜单,如果菜单有更新,就会刷新客户端的菜单。测试时可以尝试取消关注公众账号后再次关注,则可以看到创建后的效果。

2、接口请求方式:POST

3、接口:https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN

4、菜单示例:

      private final static String CREATE_MENU_URL = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";

    private static Logger logger = LoggerFactory.getLogger(WebchatMenuUtil.class);

    public static int createMenu(String token, String menu) {
        int result = 0;
        String url = CREATE_MENU_URL.replace("ACCESS_TOKEN", token);
        JSONObject jsonObject = WebchatHttpsUtil.httpsRequest(url, "POST", menu);
        if (null != jsonObject) {
            result = jsonObject.getInteger("errcode");
        }
        logger.debug("----------------公众号菜单初始化成功-------------------");
        return result;
    }

    // 默认自定义菜单初始化
    public static Menu initMenu(String backUrl, String appId) throws UnsupportedEncodingException {
        Menu menu = new Menu();
        ViewButton button11 = new ViewButton();
        button11.setName("车辆列表");
        button11.setType("view");
        button11.setUrl(backUrl + "/freeter-api/wechatredirect/auth?state=carListPage");

        ViewButton button12 = new ViewButton();
        button12.setName("报警列表");
        button12.setType("view");
        button12.setUrl(backUrl + "/freeter-api/wechatredirect/auth?state=stockListPage");

        Button button1 = new Button();
        button1.setName("数据列表"); // 将button11/button12两个button作为二级菜单封装第一个一级菜单
        button1.setSub_button(new Button[] { button11, button12 });

        // 创建公众号跳转到小程序的菜单
        MiniprogramButton miniprogramButton = new MiniprogramButton();
        miniprogramButton.setName("上线验证");
        miniprogramButton.setType("miniprogram");
        miniprogramButton.setAppid("appid");
        miniprogramButton.setUrl("http://mp.weixin.qq.com");
        miniprogramButton.setPagepath("pages/index/index");

        ViewButton button3 = new ViewButton();
        button3.setName("个人中心");
        button3.setType("view");
        button3.setUrl(backUrl + "/freeter-api/wechatredirect/auth?state=userInfoPage");

        menu.setButton(new Button[] { button1, miniprogramButton, button3 });// 将31Button直接作为一级菜单
        return menu;
    }

     

四、获取用户openid

      1、作用:

      开发者可通过OpenID来获取用户基本信息。特别需要注意的是,如果开发者拥有多个移动应用、网站应用和公众帐号,可通过获取用户基本信息中的unionid来区分用户的唯一性,因为只要是同一个微信开放平台帐号下的移动应用、网站应用和公众帐号,用户的unionid是唯一的。换句话说,同一用户,对同一个微信开放平台下的不同应用,unionid是相同的。

      2、在本项目中,采用的是通过OAuth2的方式获取用户对于当前公众号的openId。步骤如下:

           1)配置回调域名

                 

                     注意:须将MP_verify_wtfJFVgIjlkfwy2I.txt文件放在项目的根目录下

             2)构造请求url如下:https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx8888888888888888&redirect_uri=http://mascot.duapp.com/oauth2.php&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect

                     注意:1)redirect_uri表示回调地址

                                2)snsapi_userinfo表示应用授权作用域为请求用户信息,scope=snsapi_base表示不需要用户授权

                                3)state表示我们在后面需要的参数

           3)通过调用第二步的url,微信服务器会携带code和state参数将请求转发到回调地址,也就是我们在后台定义的接口,再使用code调用微信提供的接口,获取用户的openid,url如下:https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=APPSECRET&code=CODE&grant_type=authorization_code

           通过接口调用,返回详情如下:

           此时就获取到用户的openId了

五、页面跳转控制

       1、流程说明:

        在项目中,页面跳转控制是在获取到用户openid之后,根据openid判断用户是否绑定到项目,如果没有,则页面统一跳转到用户绑定页;如果已经绑定,再根据获取code时,传的state参数值,判断当前用户点击的菜单是去哪个页面,再通过重定向的方式,携带必要的参数重定向到指定的页面。

      2、示例:

    @Value("${wechat.frontUrl}")
    private String frontUrl;
    @Value("${wechat.backUrl}")
    private String backUrl;
    @Value("${wechat.appId}")
    private String appId;
    @Value("${wechat.appSecret}")
    private String appSecret;

    // 用户同意授权,获取code
    private final static String GET_CODE_URL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPIDVALUE"
            + "&redirect_uri=REDIRECTURIVALUE&response_type=code&scope=snsapi_base&state=STATEVALUE#wechat_redirect";
    
    // 获取openId地址
    private final static String GET_OPENID_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPIDVALUE"
            + "&secret=SECRETVALUE&code=CODEVALUE&grant_type=authorization_code";
    
    @Autowired
    private TbWxUserbindingService tbWxUserbindingService;
    
    @ResponseBody
       @RequestMapping(value = "/auth", method = RequestMethod.GET)
       public void GuideServlet(HttpServletRequest request, HttpServletResponse response, @RequestParam("state") String state) throws Exception {
        System.out.println("------state:" + state);
        // 设置编码
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        response.setCharacterEncoding("utf-8");
        /**
         * 第一步:用户同意授权,获取code
         */
        String redirect_uri = URLEncoder.encode("后端接口url", "UTF-8");// 授权后重定向的回调链接地址,请使用urlencode对链接进行处理(文档要求)
        // 按照文档要求拼接访问地址
        String url = GET_CODE_URL.replace("APPIDVALUE", appId).replace("REDIRECTURIVALUE", redirect_uri).replace("STATEVALUE", state);
        System.out.println("------url:" + url);
        response.sendRedirect(url);// 跳转到要访问的地址
    }
    
    @RequestMapping("/distribute")
    @ApiOperation(value = "微信端页面跳转中央控制器")
    public String distribute(HttpServletRequest request, @RequestParam("code") String code, @RequestParam("state") String state) throws UnsupportedEncodingException {
        System.out.println("----------获取到的code:" + code + "----------state:" + state);
        String openId = null;
        String returnUrl = null;
        // 第三步:同意授权,通过code换取网页授权access_token
        if (code != null) {
            // 拼接请求地址
            String url = GET_OPENID_URL.replace("APPIDVALUE", appId).replace("SECRETVALUE", appSecret).replace("CODEVALUE", code);
            System.out.println(url);
            JSONObject jsonObject = HttpUtil.httpGet(url);
            openId = jsonObject.getString("openid");
            System.out.println("返回信息:"+ jsonObject);
        }
        if (null != openId) {
            // 判断用户是否绑定
            TbWxUserbindingEntity user = tbWxUserbindingService.isAlreadyBinding(openId);
            // 用户已绑定,根据返回的state判断需要跳转的页面
            if (null != user) {
                switch (state) {
                case "carListPage":// 车辆列表
                    returnUrl = "url" + user.getUserid();
                    break;
                case "stockListPage":// 报警列表
                    returnUrl = "url" + user.getUserid();
                    break;
                case "userInfoPage":// 用户个人中心页面
                    returnUrl = "url" + user.getUserid() + "&openId=" + openId;
                    break;
                default:
                    break;
                }
            } else {
                // 用户未绑定,页面跳转到用户绑定页面
                returnUrl = "url";
            }
            System.out.println("------跳转地址为:-----" + returnUrl);
        } else {
            return "openId获取失败";
        }
        return "redirect:" + returnUrl;
    }

 

六、接收事件推送

       1、官方解读

            在微信用户和公众号产生交互的过程中,用户的某些操作会使得微信服务器通过事件推送的形式通知到开发者在开发者中心处设置的服务器地址,从而开发者可以获取到该信息。其中,某些事件推送在发生后,是允许开发者回复用户的,某些则不允许,详细内容如下:

     1)关注/取消关注事件

     2)扫描带参数二维码事件

     3)上报地理位置事件

     4)自定义菜单事件

     5)点击菜单拉取消息时的事件推送

     6)点击菜单跳转链接时的事件推送

     2、在我的项目中,目前涉及到的事件包括:

          消关注事件:用户取消关注,自动解除该用户与库融审计项目的绑定关系自定义菜单事件:用户点击菜单,请求统一指向同一个接口,获取用户的openid,判断用户是否绑定库融审计项目,如果未绑定,则页面统一跳转到用户绑定页,绑定则跳转到该菜单对应的页面。

接收普通消息:统一回复文本消息

        1)文本消息

        2)图片消息

        3)语音消息

        4)视频消息

        5)地理位置消息

        6)链接消息

2019-01-29 15:53:36 hj12312 阅读数 24218
  • 微信开发核心基础

    很多公司,企业甚至政府部分都开通了微信公众号,想要发挥微信的大价值,就必须让微信跟自己的平台或者数据库联系起来。这套《微信开发核心基础》,用一个完整的案例,告诉你微信开发流程,错误调试,如何看开发文档,学习你就可以自己做些独立开发。这个教程使用PHP为开发工具,建议你先学习子恒老师的《微信公众号使用》。学习完后,可以跟我反馈,如果好的话,会推出《微信服务号开发》教程,QQ/微信:68183131

    13127 人正在学习 去看看 秦子恒

1、微信公众号的申请

        具体申请流程此处略过,百度一下有人教你怎样一步一步申请的。

       不过,个人账号什么的限制太多,而其他又要求营业执照等,太麻烦,所以如果是小白只是想学一下的话,可以申请一个测试号,具体申请方法见:https://jingyan.baidu.com/article/e2284b2b95f423e2e6118de3.html

2、springBoot多子模块项目的搭建(具体见XXXXX)

       该demo后端采用的是springboot,搭建一个这样的项目(具体见springBoot多模块项目的搭建)虽然很简单,但重复的工作没什么意义,所以使用了脚手架(具体见利用velocity模板引擎实现脚手架),一个请求就生成一个springboot多模块项目,如果有需求,还可以直接上传到git。

3、利用业务代码生成器自动生成业务代码(具体见XXXXX)

数据库的crud等基础操作总是那么的让人觉得无奈(毕竟大部分都没什么技术含量),要是用springdata可能还好一点,用mybatis的话,特别是mybatis XML版(非注解版)的话,如果不用生成工具的话,是不是想死。而那个mybatis generator代码生成工具,本人只能说,非常一般般,局限太多了,所以特意推一波业务代码生成器(具体见:springboot多模块项目业务代码生成器),创建好数据库后,执行一个test方法,就可以生成所有的业务代码。

4、基本配置

4.1 基本配置

点击“开发者工具”-〉"开发者文档"-〉"开始开发"-〉"接入指南"-〉""

如下图所示,点击“基本配置”

 

基本配置的URL包含两个方法:get方法和post方法

get方法,验证消息的确来自微信服务器,点击“提交”按钮时,请求的就是该方法

post方法,现业务逻辑都走该方法,当然前提是上面的“提交”能成功。

4.2 url get方法的开发与验证的实现

4.2.1 get方法开发

get方法的思路:

      将填写的token与请求参数signature,timestamp拼接成字符串,然后用sha1加密,如果加密后的值等于nonce,则将请求参数echostr返回。

@RequestMapping(value = {"/connect"},method = RequestMethod.GET)
    public String connect(String signature,String timestamp,String nonce,String echostr){
        if(WexinSha1.checkSignature(signature, timestamp, nonce)) {
            return  echostr;
        }else{
            return null;
        }
    }

WexinSha1.java

package cn.inovance.wechat.utils.tool;

import java.util.Arrays;

import cn.inovance.wechat.utils.constant.WechatConstant;
import org.apache.commons.codec.digest.DigestUtils;

public class WexinSha1 {
	public static boolean checkSignature(String signature,String timestamp,String nonce) {
		String[] arr = new String[] {WechatConstant.WECHAT_TOKEN,timestamp,nonce};
		Arrays.sort(arr);
		StringBuffer content = new StringBuffer();
		for(String a:arr) {
			content.append(a);
		}
		String sign = DigestUtils.shaHex(content.toString());
		if(sign.equals(signature)) {
			return true;
		}else {
			return false;
		}
	}
}

 

4.2.2 公网映射

(土豪能直接进行域名映射的请忽略哈) 那对于普通的开发者,如何进行公网映射呢?

有花生壳,万金维,nat123,dnspod等等,具体用哪个以及具体怎么使用,自己百度哈。

5、具体业务的开发

5.1 url post方法的开发与验证的实现

微信平台和我们自己的平台的主动交互,都是通过该post方法来进行消息的回调的,下面是controller中的post方法。

@RequestMapping(value = {"/connect"},method = RequestMethod.POST)
    public String post(HttpServletRequest request){
        String result=null;
        try {
            result= messageService.reponseMessage(request);
        } catch (Exception e) {
            logger.error("自动回复出错"+e.getMessage());
        }
        return result;
    }

post最终调用了MessageService.java的 reponseMessage(HttpServletRequest request) 方法。该方法主要是被动消息的接收(比如订阅/取消订阅,扫码,用户给公众号发消息等),主动消息的结果回调(比如模板消息,群发消息的回调等),具体大致包括以下内容:

 

MessageService.java

package cn.inovance.wechat.service.message;

import cn.inovance.wechat.module.message.Message;
import com.github.pagehelper.PageInfo;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;

public interface MessageService {
    int save(Message message);
    int update(Message message);
    int delete(String id);
    int check(Message message);
    Message get(String id);
    Message getByKeyWord(String keyWord);
    List<Message> getList(Message message);
    PageInfo<Map> getListByPage(Map<String, Object> map, int page, int rows);
    String reponseMessage(HttpServletRequest request) throws Exception;
}

MessageServiceImpl.java

package cn.inovance.wechat.service.message.impl;

import cn.inovance.wechat.dao.message.MessageMapper;
import cn.inovance.wechat.module.message.EventMessage;
import cn.inovance.wechat.module.message.Message;
import cn.inovance.wechat.module.message.TextMessage;
import cn.inovance.wechat.service.message.MessageService;
import cn.inovance.wechat.utils.constant.WechatConstant;
import cn.inovance.wechat.utils.tool.XmlUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.thoughtworks.xstream.XStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.List;
import java.util.Map;
@Service
public class MessageServiceImpl implements MessageService {
    @Autowired
    private MessageMapper messageMapper;
    protected  static final Logger logger= LoggerFactory.getLogger(MessageServiceImpl.class);

    @Override
    public int save(Message message) {
        return messageMapper.save(message);
    }

    @Override
    public int update(Message message) {
        return messageMapper.update(message);
    }

    @Override
    public int delete(String id) {
        return messageMapper.delete(id);
    }

    @Override
    public int check(Message message) {
        Message message1= messageMapper.check(message);
        if(message1==null){
            return 1;
        }else{
            return 0;
        }
    }

    @Override
    public Message get(String id) {
        return messageMapper.get(id);
    }

    @Override
    public Message getByKeyWord(String keyWord) {
        return messageMapper.getByKeyWord(keyWord);
    }

    @Override
    public List<Message> getList(Message message) {
        return messageMapper.getList(message);
    }

    @Override
    public PageInfo<Map> getListByPage(Map<String, Object> map, int page, int rows) {
        PageHelper.startPage(page,rows);
        List<Map<String, Object>> collection=messageMapper.getListByPage(map);
        return new PageInfo(collection);
    }

    @Override
    public String reponseMessage(HttpServletRequest request) throws Exception {
        Map<String,String> map= XmlUtil.xmlToMap(request);
        String msgType = map.get("MsgType");
        String result=null;
        switch (msgType){
            case "text":
                result=handlerTextMessage(map);
                break;
            case "event":
                result=handlerEventMessage(map);
                break;
            default:
                break;
        }
        logger.info(result);
        return result;
    }

    public String handlerTextMessage(Map<String,String> map){
        Message message=messageMapper.getByKeyWord(map.get("Content"));
        TextMessage textMessage=new TextMessage();
        textMessage.setToUserName(map.get("FromUserName"));
        textMessage.setFromUserName(map.get("ToUserName"));
        textMessage.setCreateTime(new Date().getTime());
        textMessage.setMsgType(WechatConstant.WECHAT_MESSAGE_TYPE_TEXT);
        if(message!=null){
            textMessage.setContent(message.getResult());
        } else{
            textMessage.setContent(WechatConstant.WECHAT_HELPER);
        }
        return XmlUtil.objToXml(textMessage);
    }

    public String handlerEventMessage(Map<String,String> map){
        String result=null;
        String event=map.get("Event");
        switch (event){
            case "subscribe":
                result=handlerSubscribeEventMessage(map);
                break;
            case "unsubscribe":
                result=null;
                break;
            case "SCAN":
                result=handlerScanEventMessage(map);
                break;
            case "LOCATION":
                result=null;
                break;
            case "CLICK":
                result=null;
                break;
            case "VIEW":
                result=null;
                break;
            case "TEMPLATESENDJOBFINISH":
                result=null;
                break;
            default:
                break;
        }
        return result;
    }

    public static String handlerSubscribeEventMessage(Map<String,String> map){
        TextMessage textMessage=new TextMessage();
        textMessage.setToUserName(map.get("FromUserName"));
        textMessage.setFromUserName(map.get("ToUserName"));
        textMessage.setCreateTime(new Date().getTime());
        textMessage.setMsgType(WechatConstant.WECHAT_MESSAGE_TYPE_TEXT);
        textMessage.setContent(WechatConstant.WECHAT_SUBSCRIBE);
        return XmlUtil.objToXml(textMessage);
    }

    public static String handlerScanEventMessage(Map<String,String> map){
        TextMessage textMessage=new TextMessage();
        textMessage.setToUserName(map.get("FromUserName"));
        textMessage.setFromUserName(map.get("ToUserName"));
        textMessage.setCreateTime(new Date().getTime());
        textMessage.setMsgType(WechatConstant.WECHAT_MESSAGE_TYPE_TEXT);
        textMessage.setContent(WechatConstant.WECHAT_SCAN);
        return XmlUtil.objToXml(textMessage);
    }


}
BaseWechatMessage.java
package cn.inovance.wechat.utils.base;

import lombok.Data;
import lombok.ToString;

@Data
@ToString
public class BaseWechatMessage {
    // 开发者微信号
    private String ToUserName;
    // 发送方帐号(一个OpenID)
    private String FromUserName;
    // 消息创建时间 (整型)
    private long CreateTime;
    // 消息类型(text/image/location/link)
    private String MsgType;
}
TextMessage.java
package cn.inovance.wechat.module.message;

import cn.inovance.wechat.utils.base.BaseWechatMessage;
import lombok.Data;
import lombok.ToString;

@Data
@ToString
public class TextMessage extends BaseWechatMessage{
    // 消息id,64位整型
    private long MsgId;
    // 消息内容
    private String Content;


}
BaseModel.java
package cn.inovance.wechat.utils.base;

import lombok.Data;
import lombok.ToString;

import java.io.Serializable;
import java.util.Date;

@Data
@ToString
public class BaseModel implements Serializable {
    private static final long serialVersionUID = 1L;
    private String id;
    private String creator;
    private Date createTime;
    private String updator;
    private String updateTime;
    private String description;
}

Message.java

package cn.inovance.wechat.module.message;

import cn.inovance.wechat.utils.base.BaseModel;
import lombok.Data;
import lombok.ToString;

@Data
@ToString
public class Message extends BaseModel{
    private String keyWord;
    private String result;


}

待会儿继续。。。

6、事件的开发

7、扫码事件

8、主动推送事件

9、消息群发

10、自定义菜单

2018-12-25 12:08:43 TOP__ONE 阅读数 156
  • 微信开发核心基础

    很多公司,企业甚至政府部分都开通了微信公众号,想要发挥微信的大价值,就必须让微信跟自己的平台或者数据库联系起来。这套《微信开发核心基础》,用一个完整的案例,告诉你微信开发流程,错误调试,如何看开发文档,学习你就可以自己做些独立开发。这个教程使用PHP为开发工具,建议你先学习子恒老师的《微信公众号使用》。学习完后,可以跟我反馈,如果好的话,会推出《微信服务号开发》教程,QQ/微信:68183131

    13127 人正在学习 去看看 秦子恒

写博客向来是直接干货走起,废话不多说:

1.注册微信公众号就不多说了,

2.在进行配置公众号之前先成为开发者,成为开发者需要进行身份验证

3.进行微信公众号的配置

    1.微信AppId和AppSecret直接可以在控制平台拿到

    2.进行域名设置

JS接口安全域名: 此域名是进行调用微信js-sdk时需要验证的域名。所有js-sdk元素都有在这个域名下。不需要添加http开头。

网页授权域名:是进行关注公众号进行网页授权用的回调域名。请注意,这里填写的是域名(是一个字符串),而不是URL,因此请勿加 http:// 等协议头,但是必须指到项目上,可自行判断是否加项目名,一般线上环境不需要加项目名,本地花生壳测试环境会添加项目名;详情查看https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842

注:非测试号还需要添加*.txt文件到项目跟目录进行验证

   3.进行服务器配置

URL必须为可直接访问路径,用于微信服务器进行确认服务器是通的。

Token为自定义的和微信服务器约定值,尽量用数字。

代码实例:

URL接口实现代码

    /**
	 * 微信请求 即url
	 * @return
	 */
	@RequestMapping(value="/wechatEvent")
	public void wechatEvent(HttpServletRequest request, HttpServletResponse response){
		try {
			String signature = request.getParameter("signature");// 微信加密签名
			String timestamp = request.getParameter("timestamp");// 时间戮
			String nonce = request.getParameter("nonce"); // 随机数
			String echostr = request.getParameter("echostr"); // 随机字符串

			if(!StringUtils.isEmpty(echostr)){
			  //签名验证  此处就是处理服务器验证的,将随机串返回微信服务器
			   if(CheckSignature.checkSignature(signature, timestamp, nonce)==true) {
				  jsonOut(echostr);
			   }
		  	}else{
				ServletInputStream in=request.getInputStream();
				response.setCharacterEncoding("utf-8");
				//将微信消息转换为实体类
				EventMessage message= XMLConverUtil.convertToObject(new EventMessage().getClass(), in);
				//关注事件
				if (message.getEvent().toLowerCase().equals(MessageType.SUBSCRIBE)) {
					StringBuffer url = request.getRequestURL();
					String tempContextUrl = url.delete(url.length() - request.getRequestURI().length(), url.length()).append(request.getContextPath()).toString();
					message.setUrl(tempContextUrl);
					logger.info(message.getFromUserName());					
				}
				JsonUtil.toJSONString("接收到用户发来消息==="+message);
				//处理微信消息
				String xml= WechatServiceUtil.getWechatMessageAndSendMessageToWechat(message);
				if (!StringUtils.isEmpty(xml)){
					response.getWriter().write(xml);//回复消息
				}

			}
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("微信请求异常====》"+e);
		}
			
	}

校验方法checkSignature:

package com.chinatsp.wechat.util;

import com.chinatsp.wechat.bean.WechatBean;
import org.apache.log4j.Logger;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

public class CheckSignature {
	private Logger log = Logger.getLogger(getClass());

    /**
	 * 加密/校验
	 * @param signature
     * @param timestamp
     * @param nonce
     * @return
     */
	 public static boolean checkSignature(String signature, String timestamp, String nonce) { 
        //此处TOKEN为微信服务器后台自定义的 token值,这里只是进行了封装bean,可自行获取处理
		String[] arr = new String[] { WechatBean.TOKEN, timestamp, nonce };

		// 将token、timestamp、nonce三个参数进行字典序排序
		Arrays.sort(arr);
		StringBuilder content = new StringBuilder();
		for (int i = 0; i < arr.length; i++) {
			content.append(arr[i]);
		}

		// 将三个参数字符串拼接成一个字符串进行sha1加密
		MessageDigest md = null;
		String tmpStr = null;
		try {
			md = MessageDigest.getInstance("SHA-1");
			byte[] digest = md.digest(content.toString().getBytes());
			tmpStr = byteToStr(digest);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		content = null;

		// 将sha1加密后的字符串可与signature对比,标识该请求来源于微信
		return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;

	 }

    /**
	 *将byte数组转换成字体串
	 * @param byteArray
	 * @return
	 */
	private static String byteToStr(byte[] byteArray) {
		String strDigest = "";
		for (int i = 0; i < byteArray.length; i++) {
			strDigest += byteToHexStr(byteArray[i]);
		}
		return strDigest;
	}

	/**
     * 字节流转换为十六进制字符串
	 * @param mByte
     * @return
     */
	private static String byteToHexStr(byte mByte) {
		char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
		char[] tempArr = new char[2];
		tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
		tempArr[1] = Digit[mByte & 0X0F];
		String s = new String(tempArr);
		return s;
	}

	public String getSignature(String jsapi_ticket,String noncestr,String timestamp,String url) {
		log.info("jsapi_ticket"+jsapi_ticket);
		String signature="";
		String a="jsapi_ticket="+jsapi_ticket+"&noncestr="+noncestr+"&timestamp="+timestamp+"&url="+url;
		MessageDigest md = null;
		try {
			md = MessageDigest.getInstance("SHA-1");
			// 将三个参数字符串拼接成一个字符串进行sha1加密
			byte[] digest = md.digest(a.getBytes());
			signature = byteToStr(digest);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		return signature.toLowerCase();
	}
}

启动项目,进行服务器配置成功保存,提示配置成功即代表通讯成功。

到此,基本上配置介绍了 。

附录:下图再配一张测试公众号的配置概览图以供参考

 

 

如果看完此文章还有不明白的地方,请戳此处https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421135319

 

 

没有更多推荐了,返回首页