微信公众号从0到1开发

news2024/11/26 12:10:19

之前做项目有就接触微信公众号的接入,但没有将过程记录成笔记,这几天在做的项目也是需要集成微信公众号,正好将在做的过程记录成笔记

文章目录

    • 0、准备工作
      • 一、公众号平台
        • 1、参数解释
        • 2、获取域名
      • 二、后端配置
    • 一、第一阶段-公众号接入
      • 1、接入过程描述
      • 2、实现过程
    • 二、第二阶段-被动回复消息
      • 1、情景描述
      • 2、功能实现
    • 第三阶段-菜单配置
    • 第四阶段-菜单跳转携带openid等信息
    • 第五阶段-通知推送

0、准备工作

要想将项目接入微信公众号,需要在项目中配置公众号信息,在公众号平台绑定项目路径

一、公众号平台

在开发阶段可以使用测试号来调试开发,网站地址:微信公众平台

扫码登录进入
在这里插入图片描述

1、参数解释

  • 测试号信息

appID和appsercret相当于是公众号的唯一凭证,用来验证匹配公众号的

  • 接口配置信息修改

URL:接入公众号的项目后端接入公众号接口的地址

注意:

1、接口地址必须是域名+接口路径的形式,形如:http://xxx.xx.com/xxx/access

2、这个URL可以晚些再写,因为点击提交时平台会调用填写的URL里面的接口来验证后端是否成功接入

所以需要有个域名,获取域名方式

Token:这个命名可以随意命名,但要保证与后端配置中保持一致

  • js接口安全域名修改

域名:域名地址

注意:不要有http://,就是单纯的域名url

2、获取域名

市面上有很多内网穿透的软件工具,我使用的是natapp

里面有免费版的收费的,免费的域名会随机分配,并不定时变动,我去年用免费版的是可以调试接入公众号的,但现在用免费版的接口根本调不通了,无法白嫖了😔

收费的一个月十来块钱,是可以调通接口的,可以申请一个月玩玩

网址:https://natapp.cn/
在这里插入图片描述
申请完成后,留意authtoken信息
在这里插入图片描述
并下载他的软件

下载解压之后,双击exe文件打开
在这里插入图片描述
输入命令:

natapp -authtoken 自己的authtoken

回车打开

成功会形如:

在这里插入图片描述

这时,生成的域名便映射到localhost:8081这个地址上了,域名获取成功

注意:在后端调试过程中,这个cmd窗口不能关闭

二、后端配置

需要把微信公众号上的参数绑定到后端项目上,参数包括appid,appsecret,token,域名地址

注意:参数名称命名没有要求,只要保证对应的参数值一致就行

在这里插入图片描述

形如:

#微信公众号
##测试号
wxToken=xbwangxxx
appId=wxc1xxx
appsecret=a09c2118xxxxxxxxxx
### 域名
DNS_URl=http://xxx.natapp1.cc

准备工作完成后,便可以开始接入微信公众号了

一、第一阶段-公众号接入

1、接入过程描述

在后端编写一个接入接口,然后微信平台去调用这个接口,如果调通,则接入成功

2、实现过程

在准备工作开始时,配置了接口配置信息,现在可以开始URL的接口编写
创建接口
controller层

/**
 * 微信公众号接口
 * @author xbwang
 * @date 2023/9/05 11:14
 **/
@Controller
@RequestMapping("/wechat")
public class WeChatController {
    @Resource
    private WeChatService weChatService;

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

    /**
     * 微信公众号接入
     * @param request
     * @param response
     */
    @GetMapping("/access")
    public void wxlogin(HttpServletRequest request, HttpServletResponse response) {
        String signature = request.getParameter("signature");
        String timestamp = request.getParameter("timestamp");
        String nonce = request.getParameter("nonce");
        String echostr = request.getParameter("echostr");
        PrintWriter out=null;
        try {
            out = response.getWriter();
        } catch (IOException e) {
            logger.error("获取PrintWriter异常");
        }
        if (SignUtil.checkSignature(signature, timestamp, nonce)) {
            logger.debug("微信公众号接入成功!");
            out.print(echostr);
        }
        out.close();
    }

对应的校验签名工具类

public class SignUtil{
/**
     * 校验签名
     * @param signature 微信加密签名.
     * @param timestamp 时间戳.
     * @param nonce 随机数.
     * @return
     */
    public static boolean checkSignature(String signature, String timestamp, String nonce) {
        // 对token、timestamp、和nonce按字典排序.
        String[] paramArr = new String[] {PropertiesUtil.getProperty("wxToken"), timestamp, nonce};
        Arrays.sort(paramArr);
        // 将排序后的结果拼接成一个字符串.
        String content  = paramArr[0].concat(paramArr[1]).concat(paramArr[2]);

        String ciphertext = null;
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            // 对拼接后的字符串进行sha1加密.
            byte[] digest = md.digest(content.toString().getBytes());
            ciphertext = byteToStr(digest);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }

        // 将sha1加密后的字符串与signature进行对比.
        return ciphertext != null ? ciphertext.equals(signature.toUpperCase()) : false;
    }

    /**
     * 将字节数组转换为十六进制字符串.
     * @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;
     }
    }

编写好后,运行后端项目,URL信息就可以填写上了,形如 http://域名/(项目虚拟路径)/wechat/access
在接口处打上断点,点击提交,如果进入断点,说明接入成功
在这里插入图片描述
在这里插入图片描述

二、第二阶段-被动回复消息

1、情景描述

给公众号发送消息时,公众号会回复相应内容,形如
在这里插入图片描述
上面回复的内容就是我自定义的内容

2、功能实现

controller层

注意:接口地址要跟接入的那个接口名称一致,可以写2个接口(一个post请求,一个get请求),也可以整合到一个接口里(即1个接口具备2个功能:1、公众号接入认证,2、消息被动回复)

/**
     * 消息被动回复
     * @param request
     * @param response
     */
    @PostMapping("/access")
    public void feedbackMsg(HttpServletRequest request, HttpServletResponse response) {
        //响应消息
        PrintWriter out=null;
        try {
            response.setCharacterEncoding("utf-8");
            out = response.getWriter();
            //被动回复消息
            String feedmsg = null;
            feedmsg = weChatService.feedbackMsg(request,response);
            logger.info("被动回复消息:{}",feedmsg);
            out.print(feedmsg);
        } catch (IOException e) {
            logger.error("获取PrintWriter异常",e);
        }
        out.close();
    }

service层

/**
 * @author xbwang
 * @date 2023/9/5 14:35
 **/
@Service
public class WeChatService {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    @Resource
    private WxTokenMapper wxTokenMapper;
    @Resource
    private WxUserMapper wxUserMapper;

    public Integer updateToken(WxToken wxToken){
        return wxTokenMapper.updateByIdNoHis(wxToken);
    }

    public String feedbackMsg(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        req.setCharacterEncoding("UTF-8");
        String message = "success";
        //响应消息
        PrintWriter out=null;
        resp.setCharacterEncoding("utf-8");
        out = resp.getWriter();
        // 把微信返回的xml信息转义成map
        try {
            Map<String, String> xmlMessage = WxMessageUtil.xmlToMap(req);
            String fromUserName = xmlMessage.get("FromUserName"); // 这个就是你关注公众号的openId
            String toUserName = xmlMessage.get("ToUserName"); // 这个是用户微信的id
            String msgType = xmlMessage.get("MsgType"); // 消息类型(event或者text)
            String createTime = xmlMessage.get("CreateTime"); // 消息创建时间 (整型)
            logger.info("消息来自(公众号的openId)=>" + fromUserName);
            logger.info("用户微信的id=>" + toUserName);
            logger.info("消息类型为=>" + msgType);
            logger.info("消息创建时间 (整型)=>" + createTime);
            if ("event".equals(msgType)) { // 如果是事件推送
                String eventType = xmlMessage.get("Event"); // 事件类型
                if ("subscribe".equals(eventType)) { // 如果是订阅消息
                    String subscribeContent = "感谢关注";
                    String subscribeReturnXml = WxMessageUtil.getWxReturnMsg(xmlMessage, subscribeContent);
                    return subscribeReturnXml;
                }
                if ("SCAN".equals(eventType)) { // 如果是扫码消息
                    String scanContent = "扫码成功";
                    String scanReturnXml = WxMessageUtil.getWxReturnMsg(xmlMessage, scanContent);
                    return scanReturnXml;
                }
            }
            if ("text".equals(msgType)) { // 如果是文本消息推送
                String content = xmlMessage.get("Content"); // 接收到的消息内容,可以对特定的内容添加定制化回复             
                String textReturnXml = WxMessageUtil.getWxReturnMsg(xmlMessage, content);
                return textReturnXml; // 将接收到的文本消息变成xml格式再返回
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return message;
    }
  }

对应的工具类



/**
 * 微信消息处理类(微信消息交互大部分就是xml格式交互)
 */
@Slf4j
public class WxMessageUtil {

    /*
     * xml转map
     */
    public static Map<String, String> xmlToMap(HttpServletRequest request) throws IOException, DocumentException {
        HashMap<String, String> map = new HashMap<String,String>();
        SAXReader reader = new SAXReader();

        InputStream ins = request.getInputStream();
        Document doc = reader.read(ins);

        Element root = doc.getRootElement();
        @SuppressWarnings("unchecked")
        List<Element> list = (List<Element>)root.elements();

        for(Element e:list){
            map.put(e.getName(), e.getText());
        }
        ins.close();
        return map;
    }

    /**
     * 获取公众号回复信息(xml格式)
     */
    public static String getWxReturnMsg(Map<String, String> decryptMap, String content) throws UnsupportedEncodingException {
        log.info("---开始封装xml---decryptMap:" + decryptMap.toString());
        TextMessage textMessage = new TextMessage();
        textMessage.setToUserName(decryptMap.get("FromUserName"));
        textMessage.setFromUserName(decryptMap.get("ToUserName"));
        textMessage.setCreateTime(System.currentTimeMillis());
        textMessage.setMsgType("text"); // 设置回复消息类型
        textMessage.setContent(content); // 设置回复内容
        String xmlMsg = getXmlString(textMessage);
        // 设置返回信息编码,防止中文乱码
//        String encodeXmlMsg = new String(xmlMsg.getBytes(), "ISO-8859-1");
        String encodeXmlMsg = new String(xmlMsg.getBytes(), "GBK");
        return encodeXmlMsg;
    }

    /**
     * 设置回复消息xml格式
     */
    private static String getXmlString(TextMessage textMessage) {
        String xml = "";
        if (textMessage != null) {
            xml = "<xml>";
            xml += "<ToUserName><![CDATA[";
            xml += textMessage.getToUserName();
            xml += "]]></ToUserName>";
            xml += "<FromUserName><![CDATA[";
            xml += textMessage.getFromUserName();
            xml += "]]></FromUserName>";
            xml += "<CreateTime>";
            xml += textMessage.getCreateTime();
            xml += "</CreateTime>";
            xml += "<MsgType><![CDATA[";
            xml += textMessage.getMsgType();
            xml += "]]></MsgType>";
            xml += "<Content><![CDATA[";
            xml += textMessage.getContent();
            xml += "]]></Content>";
            xml += "</xml>";
        }
        log.info("xml封装结果=>" + xml);
        return xml;
    }
}

pojo

import lombok.Data;

/**
 * 微信消息自动回复模板类
 */
@Data
public class TextMessage {
    private String toUserName;
    private String fromUserName;
    private Long createTime;
    private String msgType;
    private String content;
}

编写好后,给测试公众号发送消息,公众号会回复相同的消息给你

第三阶段-菜单配置

第四阶段-菜单跳转携带openid等信息

第五阶段-通知推送

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/980063.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第4节-PhotoShop基础课程-Ps格式

文章目录 前言1.像素认识2. 图层认识1.图层有上下前后遮挡关系2.橡皮檫可以擦掉选择图层的像素3.新建图层4.新建删除图层 3. 分辨率的理解4. 图片格式A 前言 本章主要介绍PS常用格式 1.像素认识 下面每个格子就是像素 2. 图层认识 1.图层有上下前后遮挡关系 2.橡皮檫可以擦…

elk安装篇之 Kibana安装

Kibana是一个开源的分析与可视化平台&#xff0c;设计出来用于和Elasticsearch一起使用的。你可以用kibana搜索、查看存放在Elasticsearch中的数据。是es的可视化客户端之一。 一&#xff1a;下载 https://www.elastic.co/cn/kibana 我的es是elasticsearch-7.10.2版本&#x…

MQ解决重复消费问题

1. 消息重复消费概述 重复消费一直是行业内重视的问题&#xff0c;在当下的互联网时代&#xff0c;追求的是高效&#xff0c;安全&#xff0c;准确的数据交互。对于大型项目来讲&#xff0c;数据量数以亿计&#xff0c;那么这些数据如何确保安全准确&#xff0c;同时又不失效率…

用AI数字人视频带货新玩法教程

本期是赤辰第26期AI项目教程&#xff0c;底部准备了9月粉丝福利&#xff0c;可以免费领取。 今天给大家分享的AI项目是用AI数字人图文带货账号案例&#xff0c;这个账号是我2周前刷到的&#xff0c;今早闲着无事又刷到了这个账号数据已经飞起来了&#xff0c;第一条视频是8月1…

webhook--详解(gitee 推送)

一、简介 webhook 是一种基于 HTTP 的回调函数&#xff0c;可在 2 个应用编程接口&#xff08;API&#xff09;之间实现轻量级的事件驱动通信。是一种新型的前后端交互方式&#xff0c;一种对客户端-服务器模式的逆转&#xff0c;在传统方法中&#xff0c;客户端从服务器请求数…

提货卡礼品卡免登录提货程序开发

提货卡礼品卡免登录多活动H5小程序开发 适用于公司福利礼品卡提货&#xff0c;礼品公司提货卡。 功能&#xff1a; 支持多平台&#xff1a;基于Uniapp开发&#xff0c;可编译H5、微信小程序。 商品库模式&#xff1a;提货活动创建可以设置从商品库选择本活动可选的商品&am…

RKNPU2通用API和零拷贝API

RKNPU2通用API 通用API接口按照异构编程规范&#xff0c;需要将数据拷贝到NPU运行时的内存空间。 通用API部署流程 初始化上下文&#xff0c;需要先创建上下文对象和读取模型文件 rknn_context ctx; model load_model(model_path, &model_len); ret rknn_init(&ctx…

集合的进阶学习

集合体系结构 Collection 单列集合 包含List Set List 包含ArrayList LinkedList Set包含HashSet TreeSet HashSet包含LinkedHashSet List系列集合&#xff1a;添加的元素是有序的、可重复、有索引 Set系列集合&#xff1a;添加的元素是无序的、不重复、无索引 Collectio…

华为云云服务器评测|在Docker环境下部署Mysql数据库

华为云云服务器评测&#xff5c;在Docker环境下部署Mysql数据库 一、前言1.1 云耀云服务器L实例简介1.2 Mysql数据库简介 二、本次实践介绍2.1 本次实践简介2.2 本次环境规划 三、购买云耀云服务器L实例3.1 登录华为云3.2 购买云耀云服务器L实例3.3 查看云耀云服务器L实例状态3…

Windows wsl2安装Ubuntu

wsl&#xff08;Windows Subsystem for Linux&#xff09;即适用于Windows的Linux子系统&#xff0c;是一个实现在Windows 10 / 11上运行原生Linux的技术。 wsl2 为其迭代版本&#xff0c;可以更好的在Windows上运行Linux子系统。 这里以 Windows 11 安装Ubuntu作为示例。 开启…

浅识java多线程

目录 一 进程和线程定义 二 创建线程的种类 &#xff08;1&#xff09;继承java.lang.Thread &#xff08;2&#xff09;实现java.lang.Runnable接口 三 多线程 &#xff08;1&#xff09;继承java.lang.Thread多线程 &#xff08;2&#xff09;实现java.lang.Runnable…

vmware fusion12共享文件夹到虚拟机window10

文章目录 一、window10虚拟机安装VMware Tools二、MAC配置共享文件夹三、使用 一、window10虚拟机安装VMware Tools vmware fusion—虚拟机----安装VMware Tools–一路下一步 确认安装 双击进行安装 一路下一步&#xff0c;傻瓜式安装 二、MAC配置共享文件夹 设置—系…

2023年高教社杯全国大学生数学建模竞赛参赛事项注意

MathClub数模资源&#xff0c;含专属思路 资源链接&#xff1a;点击这里获取众多数模资料、思路精讲、论文模板latex和word、学习书籍等 2023高教社杯数学建模国赛–赛前准备 一年一度的数学建模国赛要来啦&#xff01;&#xff01;&#xff01;小编仔细阅读了比赛官方网站上…

【Java】线程都有哪几种状态

文章目录 前言传统线程模型&#xff08;操作系统&#xff09;中线程状态Java线程中的状态线程的运行流程 前言 首先我们要知道&#xff0c;在传统&#xff08;操作系统&#xff09;的线程模型中线程被分为五种状态&#xff0c;在java线程中&#xff0c;线程被分为六种状态。 …

使用客户支持自动化,您的电子商务收益稳了

经营电子商务业务涉及处理各种任务&#xff0c;从营销和库存管理到客户支持和数据分析。这些职责的复杂性可能是压倒性的&#xff0c;即使有一个专门的团队。 聊天机器人可以通过指导您的客户完成购买过程并回答他们有关产品的问题来提供更好的体验。例如SaleSmartly&#xff…

房地产推广传单制作攻略,打造让人惊艳的电子传单

随着互联网的发展&#xff0c;传统的纸质传单已经逐渐被电子版传单所取代。电子版传单不仅可以节省成本&#xff0c;还可以更好地展示房产信息。在传统的设计软件中制作电子版传单需要一定的门槛&#xff0c;但是现在有了乔拓云网的后台&#xff0c;设计电子版房产H5传单变得简…

Android——数据存储(二)(二十二)

1. SQLite数据库存储 1.1 知识点 &#xff08;1&#xff09;了解SQLite数据库的基本作用&#xff1b; &#xff08;2&#xff09;掌握数据库操作辅助类&#xff1a;SQLiteDatabase的使用&#xff1b; &#xff08;3&#xff09;可以使用命令操作SQLite数据库&#xff1b; …

RabbitMQ:hello结构

1.在Linux环境上面装入rabbitMQ doker-compose.yml version: "3.1" services:rabbitmq:image: daocloud.io/library/rabbitmq:managementrestart: alwayscontainer_name: rabbitmqports:- 6786:5672- 16786:15672volumes:- ./data:/var/lib/rabbitmq doker-compos…

Vue框架--Vue中的样式绑定

1.Vue绑定class样式属性(内部样式) 这里我们介绍三种Vue关于绑定class属性的操作。 ①.字符串写法:适用于:样式的类名不确定,需要动态指定 ②.数组写法:适用于:要绑定的样式个数不确定、名字也不确定 ③.对象写法:

vscode 调试debug rust代码的时候,中文乱码的解决办法

上次也是同样的问题&#xff0c;解决了。今天又遇到&#xff0c;我还以为是项目代码用了什么高深的地方&#xff0c;其实用chcp 65001&#xff0c;都可以解决。 一种解决方法是&#xff1a; 但建议不要用这种方法&#xff0c;因为会引起其他软件不能用&#xff08;或者出问题…