由于内容过于干燥。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
前言
上期我们介绍了用非常优雅的方式来实现发送模板消息,本期将结合工厂模式+策略模式来同样优雅的实现公众号事件处理。
首先来回顾下工厂和策略模式💻
工厂模式:
工厂模式是一种创建型设计模式,它提供了一种创建对象的最佳方式,而不必暴露对象创建的逻辑。在工厂模式中,我们定义一个工厂接口,该接口有一个或多个方法用于创建对象。然后,我们实现这个接口来创建具体的对象。这样,我们就可以在不暴露对象创建逻辑的情况下创建对象。
策略模式:
策略模式是一种行为型设计模式,它允许在运行时动态地改变对象的行为。在策略模式中,我们定义一系列算法,将每个算法封装到一个独立的类中,并让它们可以相互替换。这样可以使得算法的变化独立于使用它的客户端。
正文开始:
一、设计思想
1.事件策略:将许多类型的事件分为不同的策略,例如文本消息、图片消息、事件消息等等。
2.事件工厂:储存所有事件信息,对外提供获取事件处理器的方法。
3.事件解析:存储事件工厂,对外提供解析方法,返回解析后的实体对象WechatMessage
4.事件处理:通过工厂提供的获取事件处理器的方法,配合传来的事件类型获取具体处理器在进行消息处理。
二、结构预览
eventProcessing包,定义事件处理POJO对象、处理器、处理器工厂、解析器。
handlers包,定义多种事件消息策略处理器。
预览了结构之后,可以更好的理解下面的代码实现
三、策略消息以及实现类型
现在开始实现代码的部分,首先我们来定义事件对象、事件消息接口、基础实现
事件对象:
/**
* @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);
}
}
输出:
//测试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」定期分享有趣有料的精品原创文章!
非常感谢各位人才能看到这里,原创不易,文章如果有帮助可以关注、点赞、分享或评论,这都是对我的莫大支持!