【微信公众号入门到精通 二】生成带参数二维码以及扫码事件响应

前言

首先,为什么要生成参数二维码,他有什么用?他的使用场景是?

使用场景: 社长之前做过这样一个项目,他的主要业务,就是排课,上课,以后课文反馈。例如,课前10分钟,我们是不是需要收到短信或者公众号收到一条模板消息(上一篇文章),发消息,给谁发,是不是很关键,在当时,我们项目组的解决方法,就是在公众号里面新增一个菜单入口,通过绑定学生手机账号的方式来实现的。个人感觉有点麻烦。不太方便,个人观点,首先你可以把用户想得很懒,才能打磨出好用的产品。

  • 例如,哪些网站为什么要做微信公众号或者手机号验证码登录,如果太复杂,我是会直接放弃使用该产品的,除非没有第二选择

本文主要是生成微信参数二维码,实现学生账号和家长微信openI快速绑定, 以及能收到系统发送的消息(课后反馈)。

生成参数二维码

生成带参数的二维码demo
在这里插入图片描述

pom.xml

        <!--  httpclient-->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.13</version>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpcore</artifactId>
        </dependency>

        <dependency>
            <groupId>com.github.binarywang</groupId>
            <artifactId>weixin-java-mp</artifactId>
            <version>4.1.0</version>
        </dependency>

yml

#公众号配置
wechat:
  appid:  wx7e2be32199af1111
  appkey: 2c01014c25ed48d5a71111
  • 输入自己的公众号信息

业务处理方法

package com.zyee.iopace.web.service;

import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.zyee.iopace.web.config.WechatConfig;
import com.zyee.iopace.web.dto.vo.TemplateMsgEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

@Component
public class ConfigurationService {
    private String accessTokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&";

    @Autowired
    private WechatConfig wechatConfig;

    public String getAccessToken() {
        String requestUrl = accessTokenUrl + "appid=" + wechatConfig.getAppid() + "&secret=" + wechatConfig.getAppkey();
        String resp = HttpUtil.get(requestUrl);
        JSONObject result = JSONUtil.parseObj(resp);
        System.out.println("获取access_token:" + resp);
        String token=result.getStr("access_token");
        return token;
    }

    /**
     * 获取用户列表
     * @param accessToken
     * @return
     */

    public JSONObject getUserList(String accessToken) {
        String requestUrl = "https://api.weixin.qq.com/cgi-bin/user/get?access_token=" + accessToken + "&next_openid=";
        String resp = HttpUtil.get(requestUrl);
        JSONObject result = JSONUtil.parseObj(resp);
        System.out.println("用户列表:" + resp);
        return result;
    }

    public JSONObject sendTemplateMsg(TemplateMsgEntity messageVo, String token, String openId,String templateId) {
        String requestUrl = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token="  + token;
        Map<String,Object> content=new HashMap<>();
        JSONObject data = JSONUtil.createObj();
        //value 为需要设置的值   color为字体颜色
        data.put("title",new JSONObject().put("value",messageVo.getTitle()));
        data.put("key1",new JSONObject().put("value",messageVo.getKey1()).put("color","#173177"));
        data.put("key2",new JSONObject().put("value",messageVo.getKey2()).put("color","#173177"));
        data.put("key3",new JSONObject().put("value",messageVo.getKey3()).put("color","#173177"));
        data.put("key4",new JSONObject().put("value",messageVo.getKey4()).put("color","#173177"));
        data.put("remark",new JSONObject().put("value",messageVo.getRemark()));

        content.put("touser",openId);
        content.put("url",messageVo.getUrl());
        content.put("template_id",templateId);
        content.put("data",data);
        String resp = HttpUtil.post(requestUrl,new JSONObject(content).toString());
        System.out.println(content.toString());
        JSONObject result = JSONUtil.parseObj(resp);
        System.out.println("发送消息:" + resp);
        return result;
    }


    /**
     * 生成临时二维码
     * @param accessToken
     * @param sceneId
     * @return
     */
    public  String getTemporaryQR(String accessToken,String sceneId){
        //获取数据的地址(微信提供)
        String url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token="+accessToken+"";

        //发送给微信服务器的数据 expire_seconds为时间,单位秒,最大2592000,30天,这里设置120秒,QR_STR_SCENE标识可以设置字符串
        String jsonStr = "{\"expire_seconds\": 120,\"action_name\": \"QR_STR_SCENE\", \"action_info\": {\"scene\": {\"scene_str\": "+sceneId+"}}}";

        //将得到的字符串转化成json对象
        String ticketJson =  HttpUtil.post(url,jsonStr);

        JSONObject result = JSONUtil.parseObj(ticketJson);
        String ticket=result.getStr("ticket");
        return ticket;
    }
}

controller类

package com.zyee.iopace.web.controller;

import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.system.UserInfo;
import com.zyee.iopace.web.dto.vo.TemplateMsgEntity;
import com.zyee.iopace.web.enums.WxTemplateType;
import com.zyee.iopace.web.response.ResponseResult;
import com.zyee.iopace.web.service.ConfigurationService;
import com.zyee.iopace.web.utils.WxUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Locale;

@Slf4j
@Api(tags = "微信测试接口")
@RestController
@CrossOrigin
@RequestMapping("/wx")
public class WxController {
    @Autowired
    private ConfigurationService configurationService;

    /**
     * 创建二维码
     * @param account
     * @return
     * @throws Exception
     */
    @RequestMapping("/getImg")
    @ApiOperation("创建生成二维码")
    public void getImg(String account, HttpServletResponse resp) throws Exception {
        String accessToken = configurationService.getAccessToken();

        //真实业务代码
        //1.根据account账号信息查询redis是否存在该ticket数据,ticket有效期最大不超过2592000(即30天)
        //2、不存在调用getTemporaryQR返回ticket,存在则直接使用redis里面的请求

        //获取ticket
        String ticket = configurationService.getTemporaryQR(accessToken,account);
        String url2 = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + ticket + "";
        try {
            URL url = new URL(url2);
            HttpURLConnection httpUrl = (HttpURLConnection) url.openConnection();
            httpUrl.connect();
            httpUrl.getInputStream();
            InputStream is = httpUrl.getInputStream();
            BufferedImage image = ImageIO.read(is);
            // 可通过输出流输出到页面
            ImageIO.write(image, "png", resp.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

  • 直接访问http://localhost:9090/wx/getImg?account=‘#123’,查看结果,注意’#123’是学生的账号id,最好是开头增加一个#号,方便后面,取消关注和关注事件里面获取key的值,方便系统知道,是扫的那个学生的码。

到这里我们就可以实现扫码后,跳转到公众号,万里长征还差一步,扫码以后,我是不是需要把学生账号的信息,跟家长的openId进行绑定,这样才可以实现,家长扫码后,自动跟学生账号绑定,还有一种情况,就是家长取消关注后,需要把学生跟家长微信openId的绑定关系进行取消

内外网映射

为什么需要内外网映射? 那是因为公众号回调填写的网站是需要是域名,以及备案的。

使用工具有路由侠、花生壳(买过38一年的)等。

  • 花生壳之前可以在公众号里面使用,最近我去使用购买的域名,发现配置验证地址后,前台没有任何的影响,这种情况大概率就是域名被封咯
  • 网上免费白嫖的natapp和ngork(都试验过,通过网站可以访问,但是配置到公众号哪里,没有任何的影响应)

花生壳技术客户沟通记录
在这里插入图片描述

路由侠内网穿透工具

  • 1、下载安装路由侠
  • 2、注册登录
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  • 选择集成端口映射,ip别写127.0.0.1,写实际ip,不然会有 坑

验证

####内网访问的地址
http://localhost:9090/wx/checkSign
####外网访问的地址
http://XXXX.e1.luyouxia.net:24722/wx/checkSign
  • 实现localhost:9090和XXXX.e1.luyouxia.net:24722的映射
  • 发现后台有日志打印,说明映射成功

扫码后事件

在这里插入图片描述

  • 复制外网的访问地址,记住后面有用

微信回调方法对应代码

controller类
package com.zyee.iopace.web.controller;

import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.system.UserInfo;
import com.zyee.iopace.web.dto.vo.TemplateMsgEntity;
import com.zyee.iopace.web.enums.WxTemplateType;
import com.zyee.iopace.web.response.ResponseResult;
import com.zyee.iopace.web.service.ConfigurationService;
import com.zyee.iopace.web.utils.WxUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Locale;

@Slf4j
@Api(tags = "微信测试接口")
@RestController
@CrossOrigin
@RequestMapping("/wx")
public class WxController {
    @Autowired
    private ConfigurationService configurationService;

    /**
     *  微信公众号回调
     * @param request
     * @return
     * @throws Exception
     */
    @RequestMapping ("/checkSign")
    public String checkSign (HttpServletRequest request) throws Exception {
        //获取微信请求参数
        log.info("接收微信公众号事件触发回调请求");
        String signature = request.getParameter ("signature");
        String timestamp = request.getParameter ("timestamp");
        String nonce = request.getParameter ("nonce");
        String echostr = request.getParameter ("echostr");
        //参数排序。 token 就要换成自己实际写的 token
        String [] params = new String [] {timestamp,nonce,"123456"} ;
        Arrays.sort (params) ;
        //拼接
        String paramstr = params[0] + params[1] + params[2] ;
        //加密
        //获取 shal 算法封装类
        MessageDigest Sha1Dtgest = MessageDigest.getInstance("SHA-1") ;
        //进行加密
        byte [] digestResult = Sha1Dtgest.digest(paramstr.getBytes ("UTF-8"));
        //拿到加密结果
        String mysignature = WxUtils.byteToStr(digestResult);
        mysignature=mysignature.toLowerCase(Locale.ROOT);
        log.info("微信加密,signature:"+signature);
        log.info("本地加密,mysignature:"+mysignature);
        //是否正确
        boolean signsuccess = mysignature.equals(signature);
        //逻辑处理
        if (signsuccess && echostr!=null) {
            //peizhi  token
            return echostr  ;//不正确就直接返回失败提示.
        }else{
            String userInfo= configurationService.callback(request);
            return userInfo;
        }
    }
}
业务事件类
 package com.zyee.iopace.web.service;

import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.zyee.iopace.web.config.WechatConfig;
import com.zyee.iopace.web.dto.vo.TemplateMsgEntity;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;

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

@Component
public class ConfigurationService {
    private String accessTokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&";

    @Autowired
    private WechatConfig wechatConfig;
    /**
     * 微信回调事件
     * @param request
     * @return
     * @throws Exception
     */
    public String callback(HttpServletRequest request) throws Exception{
        WxMpXmlMessage message = WxMpXmlMessage.fromXml(request.getInputStream());
        if(message.getEvent().equals("SCAN")){
            //扫码 eventKey为扫码绑定的值,建议定义一个前缀,方便区分, qrscene_ 为未关注用户的前缀
            //为未关注用户的前缀  qrscene_bind_123  关注是不会有qrscene_前缀信息的  bind可以作为我们扫码事件的标识,区分入口,123就是对应的id

            // todo 需要提供判断事件,确定是否为扫码绑定的
        }else if(message.getEvent().equals("subscribe")){
            //关注代码
            // todo 需要提供判断事件,确定是否为扫码绑定的
        } if(message.getEvent().equals("unsubscribe")){
            //取消关注代码
            //删除openId为fromUser的数据,openId的值,从fromUser中取
        }
        System.out.println();
        return null;
    }
}


  • 这里都是伪代码 ,知道怎么实现就行。
    在这里插入图片描述

验证微信回调

打开测试微信公众号的地址
在这里插入图片描述
在这里插入图片描述

  • 通过如图生成二维码,扫码后,在checkSign方法上断点查看,是否进来。如果能进来,说明微信回调是没有问题的。
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值