【公众号开发】公众号技术分享第三期-公众号事件处理-设计模式实战分享

由于内容过于干燥。brath趁机打一波广告…

​ 由Brath全栈开发的程序员刷题应用-面试记 现已经全面开放测试。

​ 在面试记,你可以:获取与分享编程知识与面经,数十万道题目供你选择,通过喜爱标签自由组卷,模拟面试,最新支持GPT3.5免费使用…

​ 由于时间原因,更多功能还在慢慢开发中,如果你对面试记感兴趣,想要共同开发的话,请联系我的微信:【Brath_code】

面试记APP

Github:https://github.com/Guoqing815/interview

安卓APP下载:https://www.pgyer.com/interview_app_release

Brath的个人博客:https://brath.top

面试记官方公众号,定期分享有趣的编程知识:https://mp.weixin.qq.com/s/jWs6lLHl5L-atXJhHc4YvA
image-20230517151019335

前言

​ 上期我们介绍了用非常优雅的方式来实现发送模板消息,本期将结合工厂模式+策略模式来同样优雅的实现公众号事件处理。

首先来回顾下工厂和策略模式💻

工厂模式:

​ 工厂模式是一种创建型设计模式,它提供了一种创建对象的最佳方式,而不必暴露对象创建的逻辑。在工厂模式中,我们定义一个工厂接口,该接口有一个或多个方法用于创建对象。然后,我们实现这个接口来创建具体的对象。这样,我们就可以在不暴露对象创建逻辑的情况下创建对象。

策略模式:

​ 策略模式是一种行为型设计模式,它允许在运行时动态地改变对象的行为。在策略模式中,我们定义一系列算法,将每个算法封装到一个独立的类中,并让它们可以相互替换。这样可以使得算法的变化独立于使用它的客户端。

正文开始:

一、设计思想

1.事件策略:将许多类型的事件分为不同的策略,例如文本消息、图片消息、事件消息等等。
2.事件工厂:储存所有事件信息,对外提供获取事件处理器的方法。
3.事件解析:存储事件工厂,对外提供解析方法,返回解析后的实体对象WechatMessage
4.事件处理:通过工厂提供的获取事件处理器的方法,配合传来的事件类型获取具体处理器在进行消息处理。

二、结构预览

image-20230612142541419

eventProcessing包,定义事件处理POJO对象、处理器、处理器工厂、解析器。

image-20230612135531430

image-20230612135502183

handlers包,定义多种事件消息策略处理器。

image-20230612135404358

预览了结构之后,可以更好的理解下面的代码实现

三、策略消息以及实现类型

现在开始实现代码的部分,首先我们来定义事件对象、事件消息接口、基础实现

事件对象:
/**
 * @author: Brath
 * @date: 2023-06-12 08:49
 * @github: https://github.com/Guoqing815
 * @Copyright: 公众号:InterviewCoder | 博客:https://brath.top - 为了更好的你,也为了更好的世界。
 * @description: WechatMessage 微信消息聚合对象
 */
@Data
@Accessors(chain = true)
public class WechatMessage {
    // 接收方帐号(收到的OpenID)
    private String toUserName;
    // 开发者微信号
    private String fromUserName;
    // 消息创建时间 (整型)
    private long createTime;
    // 消息类型,文本为text
    private String msgType;
    // 文本消息内容
    private String content;
    // 消息id,64位整型
    private String msgId;
    // 事件类型,subscribe(订阅)、unsubscribe(取消订阅)
    private String event;
    // 事件KEY值,qrscene_为前缀,后面为二维码的参数值
    private String eventKey;
    // 二维码的ticket,可用来换取二维码图片
    private String ticket;
    // 地理位置纬度
    private String latitude;
    // 地理位置经度
    private String longitude;
    // 地理位置精度
    private String precision;
    // 图片链接(由系统生成)
    private String picUrl;
    // 图片消息媒体id,可以调用多媒体文件下载接口拉取数据。
    private String mediaId;
    // 语音格式,如amr,speex等
    private String format;
    // 视频消息缩略图的媒体id,可以调用多媒体文件下载接口拉取数据。
    private String thumbMediaId;
    // 地理位置纬度
    private String location_X;
    // 地理位置经度
    private String location_Y;
    // 地图缩放大小
    private String scale;
    // 地理位置信息
    private String label;
    // 消息标题
    private String title;
    // 消息描述
    private String description;
    // 消息链接
    private String url;
    // 音乐链接
    private String musicUrl;
    // 高质量音乐链接,WIFI环境优先使用该链接播放音乐
    private String hqMusicUrl;
    // 语音识别结果,UTF8编码
    private String recognition;
    // 加密信息,仅在使用安全模式下需要
    private String encrypt;
}
事件消息接口:
/**
 * @author: Brath
 * @date: 2023-06-12 08:49
 * @github: https://github.com/Guoqing815
 * @Copyright: 公众号:InterviewCoder | 博客:https://brath.top - 为了更好的你,也为了更好的世界。
 * @description: WechatMessageHandler 微信消息处理接口规范
 */
public interface WechatMessageHandler {

    /**
     * 处理器是否支持 ${messageType} 类型
     *
     * @param messageType
     * @return
     */
    boolean supports(String messageType);

    /**
     * 通过XML数据解析消息
     *
     * @param wechatMessage
     * @param xmlElement
     */
    void parseMessage(WechatMessage wechatMessage, Element xmlElement);

    /**
     * 处理微信消息
     *
     * @param wechatMessage
     */
    String handleMessage(WechatMessage wechatMessage, HttpServletRequest request);

    /**
     * 获取处理器名称
     *
     * @return
     */
    String getHandleName();
}
实现类:
@Data
@Component(EventConsts.TEXT_MESSAGE_HANDLER)
@ApiModel(value = "文本消息处理器")
@Accessors(chain = true)
public class TextMessageHandler implements WechatMessageHandler {

    private Logger logger = LoggerFactory.getLogger(TextMessageHandler.class);

    /**
     * 文本消息回复服务
     */
    @Resource
    private TextReplyService textReplyService;

    @Override
    public String getHandleName() {
        return EventConsts.TEXT_MESSAGE_HANDLER;
    }

    @Override
    public boolean supports(String messageType) {
        return "text".equals(messageType);
    }

    @Override
    public void parseMessage(WechatMessage wechatMessage, Element xmlElement) {
        // 解析文本消息
        String content = xmlElement.elementText("Content");
        wechatMessage.setContent(content);
    }

    @Override
    public String handleMessage(WechatMessage wechatMessage, HttpServletRequest request) {
        logger.info("收到文本消息 wechatMessage: {}", wechatMessage);
        //TODO...
    }
}

注意:EventConsts为常量,可以自己定义:

public static final String TEXT_MESSAGE_HANDLER = "textMessageHandler";
这两段代码实现了一个事件消息处理规范接口,以及具体的处理器实现

四、事件工厂,事件处理器实现

事件工厂:
/**
 * @author: Brath
 * @date: 2023-06-12 08:49
 * @github: https://github.com/Guoqing815
 * @Copyright: 公众号:InterviewCoder | 博客:https://brath.top - 为了更好的你,也为了更好的世界。
 * @description: WechatMessageHandlerFactory  微信消息处理器工厂
 */
@Component
public class WechatMessageHandlerFactory {

    /**
     * 消息事件处理器组
     */
    private List<WechatMessageHandler> messageHandlers;

    /**
     * 初始化处理器组
     *
     * @param properties
     */
    @Resource
    private void initService(WechatMessageHandler[] properties) {
        messageHandlers = Arrays.asList(properties);
    }

    /**
     * 根据消息类型获取处理器
     *
     * @param messageType
     * @return
     */
    public WechatMessageHandler getMessageHandler(String messageType) {
        return messageHandlers.stream()
                .filter(handler -> handler.supports(messageType))
                .findFirst()
                .orElse(null);
    }
}

事件处理器:
@Component
public class WechatMessageParser {

    /**
     * 处理器工厂
     */
    private static WechatMessageHandlerFactory wechatMessageHandlerFactory;

    @Resource
    private void setWechatMessageHandlerFactory(WechatMessageHandlerFactory wechatMessageHandlerFactory) {
        WechatMessageParser.wechatMessageHandlerFactory = wechatMessageHandlerFactory;
    }

    /**
     * 解析XML
     *
     * @param xml
     * @return
     */
    public static WechatMessage parse(String xml) {
        try {
            Document document = DocumentHelper.parseText(xml);
            Element rootElement = document.getRootElement();

            //获取消息类型
            String messageType = rootElement.elementText("MsgType");

            //对象转换
            WechatMessage wechatMessage = new WechatMessage()
                    .setToUserName(rootElement.elementText("ToUserName"))
                    .setFromUserName(rootElement.elementText("FromUserName"))
                    .setCreateTime(Long.parseLong(rootElement.elementText("CreateTime")))
                    .setMsgType(messageType);

            //通过工厂获取处理器,并使用处理器parseMessage方法解析当前消息以及element对象
            Optional<WechatMessageHandler> optionalMessageHandler = Optional.ofNullable(wechatMessageHandlerFactory.getMessageHandler(messageType));
            optionalMessageHandler.ifPresent(handler -> handler.parseMessage(wechatMessage, rootElement));

            return wechatMessage;
        } catch (DocumentException e) {
            throw new RuntimeException(e);
        }
    }
}
这两段代码我们实现了事件工厂以及事件解析器的实现。

到此为止,整条链路打通,可以来单元测试一下:

/**
 * @author: Brath
 * @date: 2023-06-12 08:49
 * @github: https://github.com/Guoqing815
 * @Copyright: 公众号:InterviewCoder | 博客:https://brath.top - 为了更好的你,也为了更好的世界。
 * @description: SpringRunnerTest
 */
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SpringRunnerTest {

    private Logger logger = LoggerFactory.getLogger(SpringRunnerTest.class);

    @Resource
    private WechatMessageHandlerFactory wechatMessageHandlerFactory;

    @Test
    public void testHandler() {
        //测试text策略处理器
        WechatMessageHandler textHandler = wechatMessageHandlerFactory.getMessageHandler("text");
        System.out.println(textHandler.getHandleName());

        //测试位置策略处理器
        WechatMessageHandler location = wechatMessageHandlerFactory.getMessageHandler("location");
        System.out.println(location.getHandleName());

        //测试解析text的xml数据
        WechatMessage wechatMessage = WechatMessageParser.parse("<xml><ToUserName><!				 [CDATA[gh_6ecd244c13d6]]></ToUserName>\n" +
                "<FromUserName><![CDATA[ow3gF5zkvJc097jaYvLj5uWKZZTk]]></FromUserName>\n" +
                "<CreateTime>1686546944</CreateTime>\n" +
                "<MsgType><![CDATA[text]]></MsgType>\n" +
                "<Content><![CDATA[测试]]></Content>\n" +
                "<MsgId>24145550072327732</MsgId>\n" +
                "</xml>\n");
        System.out.println(wechatMessage);

        //测试使用text策略处理器处理wechatMessage对象
        MockHttpServletRequest request = new MockHttpServletRequest();
        String content = textHandler.handleMessage(wechatMessage, request);
        System.out.println("content: " + content);
    }
}

输出:

image-20230612141441920

//测试text策略处理器
textMessageHandler
    
//测试位置策略处理器
locationMessageHandler
    
//测试解析text的xml数据   
WechatMessage(toUserName=gh_6ecd244c13d6, fromUserName=ow3gF5zkvJc097jaYvLj5uWKZZTk, createTime=1686546944, msgType=text, content=测试, msgId=null, event=null, eventKey=null, ticket=null, latitude=null, longitude=null, precision=null, picUrl=null, mediaId=null, format=null, thumbMediaId=null, location_X=null, location_Y=null, scale=null, label=null, title=null, description=null, url=null, musicUrl=null, hqMusicUrl=null, recognition=null, encrypt=null)

//测试使用text策略处理器处理wechatMessage对象
content: <xml>
  <ToUserName><![CDATA[ow3gF5zkvJc097jaYvLj5uWKZZTk]]></ToUserName>
  <FromUserName><![CDATA[gh_6ecd244c13d6]]></FromUserName>
  <CreateTime><![CDATA[1686550406094]]></CreateTime>
  <MsgType><![CDATA[text]]></MsgType>
  <FuncFlag><![CDATA[0]]></FuncFlag>
  <Content><![CDATA[测试成功]]></Content>
</xml>
测试成功~
tips:如果你觉得Brath分享的代码还可以的话,请将我分享给更多需要帮助的人~
到此为止,公众号事件处理-设计模式的知识分享就结束啦,还请同学们多多关注InterviewCoder,做一个激进的开发者,为了更好的你,也为了更好的世界!

完结撒花❀

关于我

Brath是一个热爱技术的Java程序猿,公众号「InterviewCoder」定期分享有趣有料的精品原创文章!

非常感谢各位人才能看到这里,原创不易,文章如果有帮助可以关注、点赞、分享或评论,这都是对我的莫大支持!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

程序员Brath

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

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

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

打赏作者

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

抵扣说明:

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

余额充值