【微信开发第二章】SpringBoot实现微信公众号普通消息和模板消息回复

前言

在进行微信公众号业务开发的时候,微信公众号的消息回复是非常重要的一环,而微信公众号消息回复分为:普通消息自动回复和模板消息回复。该篇文章会先使用微信测试工具过一遍流程,再使用代码进行实现,并且每一步都有记录,力争理解的同时各位小伙伴也能够实现功能

说明:因为开通微信服务号是需要营业执照和300元的,而个人号有些功能又没有,所以是比较不方便的,但是微信官方很贴心的为我们准备了测试公众号,所以我们在测试开发阶段均可以使用测试公众号来调试。

1、通过微信测试工具实现模板消息发送

详情可以看这篇文献:https://blog.csdn.net/weixin_47316183/article/details/125245315

2、接受微信公众号的消息

1、首先要在测试公众号中配置服务器配置
在这里插入图片描述
说明:这里必须绑定的是域名,内网肯定是不行的,我这里使用的是内网穿透直接映射到本地端口的,后面有时间会专门写一篇关于如何内网穿透的文章。

2、编写控制器
注意:这个接口是用来验证服务器的,接口路径必须和上面绑定的接口路径一致。

@RestController
@RequestMapping("/api/wechat/message")
public class MessageController {

    private static final String tokenEric = "Eric";

    /**
     * 服务器有效性验证
     * @param request
     * @return
     */
    @GetMapping
    public String verifyToken(HttpServletRequest request) {
        String signature = request.getParameter("signature");
        String timestamp = request.getParameter("timestamp");
        String nonce = request.getParameter("nonce");
        String echostr = request.getParameter("echostr");
        log.info("signature: {} nonce: {} echostr: {} timestamp: {}", signature, nonce, echostr, timestamp);
        if (this.checkSignature(signature, timestamp, nonce)) {
            log.info("token ok");
            return echostr;
        }
        return echostr;
    }

    private boolean checkSignature(String signature, String timestamp, String nonce) {
        String[] str = new String[]{tokenEric, timestamp, nonce};
        //排序
        Arrays.sort(str);
        //拼接字符串
        StringBuffer buffer = new StringBuffer();
        for (int i = 0; i < str.length; i++) {
            buffer.append(str[i]);
        }
        //进行sha1加密
        String temp = SHA1.encode(buffer.toString());
        //与微信提供的signature进行匹对
        return signature.equals(temp);
    }
}

OK,完成之后,我们的校验接口就算是开发完成了。接下来就可以开发消息接收接口了。

3、编写,接收微信服务器发送来的消息

   /**
     * 接收微信服务器发送来的消息
     * @param request
     * @return
     * @throws Exception
     */
    @PostMapping
    public String receiveMessage(HttpServletRequest request) throws Exception {

        WxMpXmlMessage wxMpXmlMessage = WxMpXmlMessage.fromXml(request.getInputStream());
        System.out.println(JSONObject.toJSONString(wxMpXmlMessage));
        return "success";
    }

    private Map<String, String> parseXml(HttpServletRequest request) throws Exception {
        Map<String, String> map = new HashMap<String, String>();
        InputStream inputStream = request.getInputStream();
        SAXReader reader = new SAXReader();
        Document document = reader.read(inputStream);
        Element root = document.getRootElement();
        List<Element> elementList = root.elements();
        for (Element e : elementList) {
            map.put(e.getName(), e.getText());
        }
        inputStream.close();
        inputStream = null;
        return map;
    }

到这里,基本完成一半了,因为已经能够接受到微信服务器发来的消息的,先给大家测试一波。

给测试公众号发送消息:
在这里插入图片描述

查看控制台,发现成功接受到了消息
在这里插入图片描述

3、代码实现普通消息的发送

那么既然能够接受到微信服务器发来的消息,同样的,也能够返回消息,这里根据自己的需求来写就好了,我这里的需求是根据关键字查询对应课程信息,大家可以根据我的代码模板来进行修改,这样更方便。

我这里为了大家看的方便直接将修改后的代码放在Controller中了

public String receiveMessage(Map<String, String> param) {
    String content = "";
    try {
        //消息类型
        String msgType = param.get("MsgType");
        switch(msgType){
            case "text" :		//普通文本类型,例如用户发送:Java
                content = this.search(param);
                break;
            case "event" :		//多类型:关注、取消关注、点击菜单导航
                String event = param.get("Event");
                String eventKey = param.get("EventKey");
                if("subscribe".equals(event)) {//关注公众号
                    content = this.subscribe(param);
                } else if("unsubscribe".equals(event)) {//取消关注公众号
                    content = this.unsubscribe(param);
                } else if("CLICK".equals(event) && "aboutUs".equals(eventKey)){
                    content = this.aboutUs(param);
                } else {
                    content = "success";
                }
                break;
            default:
                content = "success";
        }
    } catch (Exception e) {
        e.printStackTrace();
        content = this.text(param, "请重新输入关键字,没有匹配到相关视频课程").toString();
    }
    return content;
}

}

/**
 * 处理关键字搜索事件
 * 图文消息个数;当用户发送文本、图片、语音、视频、图文、地理位置这六种消息时,开发者只能回复1条图文消息;其余场景最多可回复8条图文消息
 * @param param
 * @return
 */
private String search(Map<String, String> param) {
    String fromusername = param.get("FromUserName");
    String tousername = param.get("ToUserName");
    String content = param.get("Content");

    //这个判断是为靓仔专属设定的
    if("Eric".equals(content)){
        StringBuffer text = this.text(param, "这是一位神奇的靓仔!");
        return text.toString();
    }

    //单位为秒,不是毫秒
    Long createTime = new Date().getTime() / 1000;
    StringBuffer text = new StringBuffer();
    //远程调用接口:根据课程名称查询课程信息
    List<Course> courseList = courseFeignClient.findByKeyword(content);
    if(CollectionUtils.isEmpty(courseList)) {
        //如果集合等于null,说明该关键字不存在对应课程,提升用户没有匹配到相关视频课程
        text = this.text(param, "同学你好,你回复的关键词不是有效关键词!");
    } else {
        //一次只能返回一个
        Random random = new Random();
        int num = random.nextInt(courseList.size());
        Course course = courseList.get(num);
        StringBuffer articles = new StringBuffer();
        articles.append("<item>");
        articles.append("<Title><![CDATA["+course.getTitle()+"]]></Title>");
        articles.append("<Description><![CDATA["+course.getTitle()+"]]></Description>");
        articles.append("<PicUrl><![CDATA["+course.getCover()+"]]></PicUrl>");
        articles.append("<Url><![CDATA[http://glkt.atguigu.cn/#/liveInfo/"+course.getId()+"]]></Url>");
        articles.append("</item>");

        text.append("<xml>");
        text.append("<ToUserName><![CDATA["+fromusername+"]]></ToUserName>");
        text.append("<FromUserName><![CDATA["+tousername+"]]></FromUserName>");
        text.append("<CreateTime><![CDATA["+createTime+"]]></CreateTime>");
        text.append("<MsgType><![CDATA[news]]></MsgType>");
        text.append("<ArticleCount><![CDATA[1]]></ArticleCount>");
        text.append("<Articles>");
        text.append(articles);
        text.append("</Articles>");
        text.append("</xml>");
    }
    return text.toString();
}

这是按照微信官方要求格式来封装的,感兴趣的朋友可以查看官方文档:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html

测试

1、重启项目
2、给微信测试服务号发送消息
在这里插入图片描述
可以看到成功实现啦~
当然,在实际开发中,可能这个自动回复直接使用微信管理后台设置了,而我们代码实现回复更多的是模板回复,请大家耐心往下看

4、公众号模板消息

先说下实现目标:购买课程支付成功后微信自动推送支付成功消息

官方文档参考链接:https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Template_Message_Interface.html

1、先在测试公众号添加消息模板
在这里插入图片描述
这里怕有些小伙伴不熟悉,我把我的模板贴上~

{{first.DATA}} 
订单编号:{{keyword1.DATA}} 
商品名称:{{keyword2.DATA}} 
支付时间:{{keyword3.DATA}} 
支付金额:{{keyword4.DATA}}
{{remark.DATA}}

2、编写Service接口
需要说明的是,发送消息是需要指定用户的,而在微信公众号中openId就是用户的唯一标识

void pushPayMessage(Long orderId);

3、实现ServiceImpl实现类

//TODO 暂时写成固定值测试,后续完善
@SneakyThrows
@Override
public void pushPayMessage(long orderId) {
    String openid = "o1RCX6uM7uSm8-SS-eTRf13EEQ";
    WxMpTemplateMessage templateMessage = WxMpTemplateMessage.builder()
            .toUser(openid)//要推送的用户openid
            .templateId("BoSU1mzQBkC-jiwCm0SHRGZX37wRaVjcK9SlcqZL1l4")//模板id
            .url("https://zhult.com/#/pay/"+orderId)
            .build();
    //3,如果是正式版发送消息,,这里需要配置你的信息
    templateMessage.addData(new WxMpTemplateData("first", "亲爱的用户:您有一笔订单支付成功。", "#272727"));
    templateMessage.addData(new WxMpTemplateData("keyword1", "ZP235678692123", "#272727"));
    templateMessage.addData(new WxMpTemplateData("keyword2", "Java基础课程", "#272727"));
    templateMessage.addData(new WxMpTemplateData("keyword3", "2022-11-11", "#272727"));
    templateMessage.addData(new WxMpTemplateData("keyword4", "199", "#272727"));
    templateMessage.addData(new WxMpTemplateData("remark", "感谢您购买课程,如有疑问,随时咨询!", "#272727"));
    String msg = wxMpService.getTemplateMsgService().sendTemplateMsg(templateMessage);
    System.out.println("模板信息发送成功:" + msg);
}

4、编写Controller

/**
  * 发送模板消息
  * @return
  * @throws WxErrorException
  */
 @GetMapping("/pushPayMessage")
 public Result pushPayMessage() throws WxErrorException {
     messageService.pushPayMessage(1L);
     return Result.ok().message("获取模板消息成功");
 }

5、测试:因为使用的是get请求,所以我们可以直接使用浏览器测试
在这里插入图片描述

结果如下:
在这里插入图片描述

测试成功~


总结

怎么样,是不是特别简单呢,完结撒花~

【微信开发第一章】SpringBoot实现微信公众号创建菜单,同步菜单功能:https://blog.csdn.net/weixin_47316183/article/details/127821095?spm=1001.2014.3001.5502

【微信开发第二章】SpringBoot实现微信公众号普通消息和模板消息回复:https://blog.csdn.net/weixin_47316183/article/details/127821653?spm=1001.2014.3001.5502

【微信开发第三章】SpringBoot实现微信授权登录
https://blog.csdn.net/weixin_47316183/article/details/127833802?spm=1001.2014.3001.5502

【微信开发第四章】SpringBoot实现微信H5支付
https://blog.csdn.net/weixin_47316183/article/details/127949620?spm=1001.2014.3001.5502

【微信开发第五章】SpringBoot实现微信分享
https://blog.csdn.net/weixin_47316183/article/details/127950090?spm=1001.2014.3001.5502

Spring Boot 是一种用于开发 Java 应用程序的框架,它简化了传统 Java 开发的繁琐过程,使开发人员可以更快速地构建高效的应用程序。UniApp 是一个跨平台的开发框架,它可以同时开发 Android、iOS Web 应用程序,极大地提高了开发效率项目的可维护性。 微信公众号开发是指基于微信平台的应用程序开发,通过微信公众号,我们可以实现与用户的互动交流、推送消息、提供各种服务等。 在使用 Spring Boot UniApp 进行微信公众号开发时,可以采用前后端分离的开发模式。前端使用 UniApp 进行界面设计用户交互的开发后端使用 Spring Boot 进行业务逻辑的处理数据的存储。 首先,我们需要在微信公众平台注册一个开发者账号,获取到相应的公众号信息接口权限。 接下来,前端开发人员可以使用 UniApp 进行公众号的界面设计交互逻辑的编写。UniApp 提供了丰富的组件模板,可以方便地实现各种界面效果,并且可以使用 Vue.js 进行数据的绑定与处理。 后端开发人员使用 Spring Boot 进行接口的开发业务逻辑的处理。可以使用 Spring Boot 提供的丰富的功能插件来简化开发,比如使用 Spring Data JPA 来操作数据库,使用 Spring Security 来实现用户认证与权限控制等。 最后,前后端通过接口进行数据的传输交互,前端将用户的操作发送后端进行处理,并将后端返回的数据展示给用户。 通过采用 Spring Boot UniApp 进行微信公众号开发,可以充分发挥两者的优势,快速构建高效的应用程序,实现与用户的互动服务。同时,由于使用的是跨平台的开发框架,可以方便地同时开发多个平台的应用程序,提高开发效率项目的可维护性。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Eric-x

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值