微信公众号快速开发(三)多种消息类型处理

之前介绍了自动回复,下面介绍一些常见的消息处理样式

开始开发——关键字回复

功能描述

当我们公众号发送一些关键词的时候,公众号会回复自动回复有关关键词的信息。

实现思路

  1. 消息类型为文本样式
  2. 接收的客服端消息中要包含该关键字

代码开发

为便于扩展,将消息处理的方法写到服务层,新建收发信息的dto

一、便于扩展为不同类型的消息,修改收发消息的封装,改用dto模式

  • 基础消息实体类
@Data
@XmlAccessorType(XmlAccessType.FIELD) // 映射类中的所有字段到XML
public class MsgSendEntity {
    /**
     * 公有部分
     */
    //  开发者微信号
    @XmlElement(name = "ToUserName") // 指定名称映射
    private String toUserName;

    // 发送方帐号(一个OpenID)
    @XmlElement(name = "FromUserName")
    private String fromUserName;

    // 消息创建时间 (整型)
    @XmlElement(name = "CreateTime")
    private Long createTime;

    // 消息类型
    @XmlElement(name = "MsgType")
    private String msgType;

    // 消息id,64位整型
    @XmlElement(name = "MsgId")
    private Long msgId;

}
@Data
@XmlAccessorType(XmlAccessType.FIELD)
public class MsgReplyEntity {
    //  用户的OpenID
    @XmlElement(name = "ToUserName")
    private String toUserName;

    // 测试号的微信号
    @XmlElement(name = "FromUserName")
    private String fromUserName;

    // 消息创建时间 (整型)
    @XmlElement(name = "CreateTime")
    private Long createTime;

    // 消息类型
    @XmlElement(name = "MsgType")
    private String msgType;

    // 文本消息内容
    @XmlElement(name = "Content")
    private String content;
}
  • 新建消息实体类的dto
@Data
@XmlRootElement(name = "xml") // 根节点
@XmlAccessorType(XmlAccessType.FIELD) // 映射类中的所有字段到XML
public class MsgSendDto extends MsgSendEntity {

    // 文本消息内容
    @XmlElement(name = "Content")
    private String content;
}
@Data
@XmlRootElement(name="xml")
@XmlAccessorType(XmlAccessType.FIELD)
public class MsgReplyDto extends MsgReplyEntity {

}

二、抽取消息处理的服务类与消息处理的方法到服务层

@Service
public class MsgHandleServiceImpl implements IMsgHandleService {
    
    @Override
    public MsgReplyEntity handle(MsgSendEntity msgSend) {
        WeChatUtil.getLogger().info("客户端接收的内容为:{}"+msgSend);

        // 服务端消息回复的实体类
        MsgReplyEntity msgReply = new MsgReplyEntity();
        // 根据接收的信息回复,接收和发送方相反
        msgReply.setFromUserName(msgSend.getToUserName());
        msgReply.setToUserName(msgSend.getFromUserName());
        msgReply.setCreateTime(new Date().getTime());

        String msgType = msgSend.getMsgType();
        String contentReply = null;
        // 处理不同类型的消息
        if (msgType.equals(WeChatConstants.MSG_TYPE_TEXT)) {
            // 默认回复相同的类型消息
            msgReply.setMsgType(msgType);
            String contentSend = msgSend.getContent();

            // 关键词处理
            if (contentSend.contains("你好")) {
                contentReply = "你好吗\r\nhow are you";
            } else if (contentSend.contains("哈哈")||contentSend.contains("haha")) {
                contentReply = "我也喜欢哈哈大笑";
            } else if (contentSend.contains("chet")){
                msgReply.setMsgType(WeChatConstants.MSG_TYPE_NEWS);
                //设置图文个数
                msgReply.setArticleCount(1);
                //设置图文明细列表
                ArticleItem item = new ArticleItem();
                item.setTitle("chet的github博客");
                item.setPicUrl("https://chetwhy.github.io/");
                item.setDescription("chet的掘金博客");
                item.setUrl("https://juejin.im/timeline");
                msgReply.setItem(new ArticleItem[]{item});
            }else {
                // 非关键字,原样返回
                contentReply = msgSend.getContent();
            }
            msgReply.setContent(contentReply);
        }
        
        WeChatUtil.getLogger().info("服务端回复的内容为:{}"+msgReply);
        return msgReply;
    }
}

三、封装的常量类

public class WeChatConstants {

    /**
     * 公众号appid
     */
    public static String APP_ID = "wxa02348cd5ec17d28"; 

    /**
     * AppSecret
     */
    public static String APPSECRET = "***"; 

    /**
     * 公众号配置相关
     */
    public static final String URL = "ups.tiaodu.cn";
    public static final String TOKEN = "123qwe";

    /**
     * 消息类型
     */
    public static final String MSG_TYPE_TEXT = "text";
    public static final String MSG_TYPE_NEWS = "news";
}

测试样例

在手机微信或电脑微信直接发送带【关键字】的信息即可

image

开始开发——接收事件推送

功能描述

微信公众号有多种不同事件信息,包括其触发事件的类型,响应处理。最常见的,当我们点击关注某公众号之后,公众号将自动推送给我们介绍信息后者活动宣传等。

实现思路

一、参考微信公众平台技术文档->消息管理->接收事件推送

image

二、查看对应消息事件格式,扩展消息实体的dto

image

三、在原消息基础上,添加事件的逻辑判断

下面以关注/取消事件和自定义菜单事件做演示

代码开发

1为MsgSendDto添加事件属性

...
public class MsgSendDto {
    ...
    // 事件类型 subscribe(订阅)、unsubscribe(取消订阅)、CLICK(点击菜单)
    @XmlElement(name = "Event")
    private String event;
}    

2增加常量类

public class WeChatConstants {
    ...
        
    public static final String MSG_TYPE_EVENT = "event";
    public static final String MSG_TYPE_EVENT_SUBSCRIBE = "subscribe";
}    

3消息处理方法,增加判断逻辑

@Service
public class MsgHandleServiceImpl {

    public MsgReplyEntity handle(MsgSendEntity msgSend) {
        ...
           
        // 处理不同类型的消息
        if (msgType.equals(WeChatConstants.MSG_TYPE_TEXT)) {
            ...
        }else if(msgType.equals(WeChatConstants.MSG_TYPE_EVENT)){
            // 订阅事件
            if(msgSend.getEvent().equals(WeChatConstants.MSG_TYPE_EVENT_SUBSCRIBE)){
                msgReply.setMsgType(WeChatConstants.MSG_TYPE_TEXT);
                msgReply.setContent("感谢关注chetwhy![亲亲]\r\n现在回复【chet】\r\n马上查阅java博客![大兵]");
            }
        }

        WeChatUtil.getLogger().info("服务端回复的内容为:{}"+msgReply);
        return msgReply;
    }
}

测试样例

一、先取消对测试公众的关注(断点调试依然可以看到消息类型为event)

二、在测试号管理中再次扫描二维码关注

image

代码开发

开始开发——自定义菜单及其事件

功能描述

当我们点开一个订阅的公共号时,点击聊天输入框最左侧的按钮,可以切换到公众号的菜单栏,有的菜单选项中多个子菜单,有的菜单选择会自动跳转到其他页面。这为我们的公众号提供更为便捷的窗口和功能的扩展。

实现思路——自定义菜单

一、参考微信公众平台技术文档->自定义菜单->【自定义菜单...接口】和消息管理->接收事件推送->[4-6菜单事件]

image

二、按照文档,我们应先创建自定义的菜单。简单的说:

  • 菜单分为一级菜单和二级菜单,一级最多3个,二级最多5个;
  • 菜单借口大致有10种类型,分为按钮(click,view),扫码(scancode_push,scancode_waitmsg),拍照相册(pic_sysphoto,pic_photo_or_album,pic_weixin),位置(location_select),文件(media_id)等
  • post请求,https协议,请求参数需携带access_token

公众平台以access_token为接口调用凭据,来调用接口,所有接口的调用需要先获取access_token,access_token在2小时内有效,过期需要重新获取,但1天内获取次数有限,开发者需自行存储

三、根据请求示例,封装好我们自定义的json数据

四、根据文档->获取access_token,编写工具类获取返回的token

image

代码开发

1封装自定义菜单的json数据

 {
     "button":[
     {    
          "type":"click",
          "name":"今日歌曲",
          "key":"V1001_TODAY_MUSIC"
      },
      {
           "name":"菜单",
           "sub_button":[
           {    
               "type":"view",
               "name":"搜索",
               "url":"http://www.soso.com/"
            },
            {
                 "type":"miniprogram",
                 "name":"wxa",
                 "url":"http://mp.weixin.qq.com",
                 "appid":"wx286b93c14bbf93aa",
                 "pagepath":"pages/lunar/index"
             },
            {
               "type":"click",
               "name":"赞一下我们",
               "key":"V1001_GOOD"
            }]
       }]
 }

起名字费劲,我这里照搬的微信的菜单名,两个按钮型一级菜单,其中有两个子菜单

2创建获取access token的工具类方法

public class WeChatUtil {
    // 获取access_token的路径模板
    public static final String GET_ACCESSTOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
    
    public static String accessToken;

    public static long expiresTime;
    
    /**
     * 获取access_token
     * @return access_token
     */
    public static String getAccessToken(){
        // 第一次获取或access token已过期
        if(accessToken==null||new Date().getTime()>expiresTime){
            // 替换示例种参数,发送https的get请求
            String result = HttpUtil.get(GET_ACCESSTOKEN_URL.replace("APPID", WeChatConstants.APP_ID).replace("APPSECRET", WeChatConstants.APPSECRET));
            JSONObject json = JSONObject.parseObject(result);
            accessToken = json.getString("access_token");
            // 有效事件,单位秒
            Long expires_in = json.getLong("expires_in");
            // 设置凭据的失效时间,默认7200s,提前五分钟过期
            expiresTime = new Date().getTime()+((expires_in-60*5)*1000);
            WeChatUtil.getLogger().info("access_token={},expires_time={}",accessToken,expiresTime);
        }
        return accessToken;
    }
}    

3船舰自定菜单的工具类方法

public class WeChatUtil {
    
    // 自定义菜单接口
    public static final String CREATE_MENU_URL = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";
    
    /**
     * 创建自定义菜单
     * @param menuJson
     */
    public static void createMenu(String menuJson){
        //发起请求到指定的接口,并且带上菜单json数据
        String result = HttpUtil.post(CREATE_MENU_URL.replace("ACCESS_TOKEN",getAccessToken()), menuJson);
        WeChatUtil.getLogger().info("创建自定义菜单结果:{}", result);
    }
}    

4.写一个主方法,将之前封装的json传入createMenu方法。运行即可

public static void main(String[] args) {
    String menu = "...";
    createMenu(menu);
}

测试样例

一、直接运行上述的main方法,查看运行日志,生成成功

image

二、查看微信客户端的聊天页面,点开二级菜单

image

三、若日志显示成功,客户端没反应,尝试重新关注订阅号,或重启natapp

实现思路——自定义菜单事件

一、封装菜单事件的参数,扩展dto

二、增加消息处理的业务逻辑

代码开发

一、消息发送实体类

public class MsgSendDto extends MsgSendEntity {
    ...
    
    // 菜单的key值
    @XmlElement(name = "EventKey")
    private String eventKey;
}


二、消息处理方法,key即为json中的"key"键

@Service
public class MsgHandleServiceImpl implements IMsgHandleService {

    @Override
    public MsgReplyEntity handle(MsgSendDto msgSend) {
        ...
        
        // 处理不同类型的消息
        if (msgType.equals(WeChatConstants.MSG_TYPE_TEXT)) {
            ...
                
        }else if(msgType.equals(WeChatConstants.MSG_TYPE_EVENT)){
            // 订阅事件
            if(msgSend.getEvent().equals(WeChatConstants.MSG_TYPE_EVENT_SUBSCRIBE)){
                ...
            }else if(msgSend.getEvent().equals(WeChatConstants.MSG_TYPE_EVENT_CLICK)){
                String eventKey = msgSend.getEventKey();
                //判断按钮的key值
                if ("V1001_TODAY_MUSIC".equals(eventKey)){
                    contentReply = "《年少有为》- 李荣浩\n" +
                            "《The Spectre》- Alan Walker";
                }else if("V1001_GOOD".equals(eventKey)){
                    contentReply = "谢谢您的点赞关注[拇指]";
                }
                msgReply.setMsgType("text");
                msgReply.setContent(contentReply);
            }
        }

        WeChatUtil.getLogger().info("服务端回复的内容为:{}"+msgReply);
        return msgReply;
    }
}

测试样例

一、运行springboot

二、点开微信菜单栏,点击菜单按钮

image

开始开发——发送模板信息

功能描述

这个也很常见,比如当我们在公众号平台购买商品后,平台会发送下单结果的通知信息,这个类似与邮寄一样,也是模板信息。文档也说,模板消息仅用于公众号向用户发送重要的服务通知,只能用于符合其要求的服务场景中,如信用卡刷卡通知,商品购买成功通知等。

实现思路

一、参考微信公众平台技术文档->消息管理->模板消息接口

image

二、在测试公众号中配置新增模板

image

三、编写工具类方法,支持https的post请求,url为:

https://api.weixin.qq.com/cgi-bin/template/api_set_industry?access_token=ACCESS_TOKEN

代码开发

一、测试公众号->模板消息接口->新增配置模板

我这里依然使用官方文档的例子

image

如图

image

)

二、封装发送模板信息的json数据,相关信息都改成自己的,template_id即上面的【模板ID】

{
    "touser":"o50E15lhQXW0SlsYg3bKFrywtKC8",
    "template_id":"RXl8FLezLbHaBrPWTwK295CNgkNpR69Et40K3oOoK0",
    "url":"http://weixin.qq.com/download",  
    "miniprogram":{
        "appid":"xiaochengxuappid12345",
        "pagepath":"index?foo=bar"
    },          
    "data":{
        "first": {
            "value":"恭喜你购买成功!",
            "color":"#173177"
        },
        "keyword1":{
            "value":"巧克力",
            "color":"#173177"
        },
        "keyword2": {
            "value":"39.8元",
            "color":"#173177"
        },
        "keyword3": {
            "value":"2014年9月22日",
            "color":"#173177"
        },
        "remark":{
            "value":"欢迎再次购买!",
            "color":"#173177"
        }
    }
}

三、创建发送模板信息的方法

// 发送模板消息的接口
public static final String SEND_TEMPLATE_URL = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN";

/**
  * 发送模板信息
  * @param data 模板json数据
  */
public static void sendTemplate(String data){
    String result = HttpUtil.post(SEND_TEMPLATE_URL.replace("ACCESS_TOKEN", getAccessToken()),data);
    WeChatUtil.getLogger().info("发送模板消息结果:{}",result);
}

测试样例

使用第二步的json数据,直接在main方法测试即可

image

详细过程,可参考源代码(持续更新):https://github.com/chetwhy/cloud-flow

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,639评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,093评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,079评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,329评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,343评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,047评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,645评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,565评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,095评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,201评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,338评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,014评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,701评论 3 332
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,194评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,320评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,685评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,345评论 2 358

推荐阅读更多精彩内容

  • 微信服务号开发 整体流程 域名报备,服务器搭建 Python开发环境和项目的初始化搭建; 微信公众号注册及开发模式...
    飞行员suke阅读 4,519评论 0 14
  • 开发前首先我们要知道一些概念 各公众号区别:1、订阅号:为媒体和个人提供一种信息传播方式,主要偏于为用户传达资讯(...
    CoderZS阅读 3,194评论 1 19
  • 一、公众号介绍 微信公众号分类 订阅号:主要偏于为用户传达资讯(类似报纸杂志),认证前后都是每天只可以群发一条消息...
    小花的胖次阅读 6,421评论 3 37
  • 一、前言 微信公众号开发 (1) 微信接入认证成为开发者 微信公众号开发 (2) 消息处理 本文将实现 根据App...
    zhengqingya阅读 397评论 0 2
  • 大家好,我是IT修真院深圳分院第6期学员,一枚正直善良的JAVA程序员。 今天给大家分享一下,修真院官网复盘项目中...
    blue_gogogo阅读 583评论 1 3