拼一个对象,触发事件,处理事件。面向事件编程。
Entity实体类中编写消费者和生产者
创建event包,专门处理,里面的eventproducer发送消息,eventconsumer处理消息
Eventconsumer中,把从producer那边拿到的消息进行内容和格式的判断,之后发送站内通知,先设置发送者和接受者,为了获得发送的消息的具体内容,发送的消息的内容需要拼出,创这个hashmap(content),从event里面获取内容装进去,构造好消息,最后用messageservice塞进去
给响应的controller调用eventproducer,产生通知。
开发流程
event实体类,要做一定的处理,方便之后设置参数
public class Event {
private String topic; //从卡夫卡的角度来看
private int userId;
private int entityType; //事件的发生属性等等
private int entityId;
private int entityUserId;
private Map<String, Object> data = new HashMap<>(); //其他不知道的属性存在这个map里面。让其具有扩展性。
//对下面的set和get进行改造
public String getTopic() {
return topic;
}
public Event setTopic(String topic) {
this.topic = topic;
return this;
}
public int getUserId() {
return userId;
}
public Event setUserId(int userId) {
this.userId = userId;
return this;
}
public int getEntityType() {
return entityType;
}
public Event setEntityType(int entityType) {
this.entityType = entityType;
return this;
}
public int getEntityId() {
return entityId;
}
public Event setEntityId(int entityId) {
this.entityId = entityId;
return this;
}
public int getEntityUserId() {
return entityUserId;
}
public Event setEntityUserId(int entityUserId) {
this.entityUserId = entityUserId;
return this;
}
public Map<String, Object> getData() {
return data;
}
public Event setData(String key, Object value) {
this.data.put(key, value);
return this;
}
}
EventProducer
@Component
public class EventProducer {
//
@Autowired
private KafkaTemplate kafkaTemplate;
//处理事件
public void fireEvent(Event event){
//将事件发布到指定的主题
kafkaTemplate.send(event.getTopic(), JSONObject.toJSONString(event));//消息内容变化为JSON格式
}
}
EventConsumer
@Component
public class EventConsumer implements CommunityConstant {
private static final Logger logger = LoggerFactory.getLogger(EventConsumer.class);
@Autowired
private MessageService messageService;
//
@KafkaListener(topics = {TOPIC_COMMENT,TOPIC_LIKE,TOPIC_FOLLOW})
public void handleCommentMessage(ConsumerRecord record){
if (record == null || record.value() == null){
logger.error("消息的内容为空");
return;
}
Event event = JSONObject.parseObject(record.value().toString(),Event.class);
if (event == null){
logger.error("消息的格式错误");
return;
}
//发送站内通知,构造message对象
Message message = new Message();
message.setFromId(SYSTEM_USER_ID);//是系统发送通知
message.setToId(event.getEntityUserId()); //发送给的人的ID
message.setConversationId(event.getTopic());
message.setCreateTime(new Date());
//构造的具体的通知内容,最后存成json字符串传出去
Map<String,Object> content = new HashMap<>();
content.put("userId",event.getUserId());
content.put("entityType",event.getEntityType());
content.put("entityId",event.getEntityId());
if (!event.getData().isEmpty()){
//如果不为空
for (Map.Entry<String,Object> entry : event.getData().entrySet()){
content.put(entry.getKey(),entry.getValue());//把event中的其他数据在存到content中
}
}
message.setContent(JSONObject.toJSONString(content));
messageService.addMessage(message);
}
}
在Comment和Like、Follow的controller中增加发送消息的逻辑
@RequestMapping(path = "/add/{discussPostId}",method = RequestMethod.POST)
public String addComment(@PathVariable("discussPostId") int discussPostId, Comment comment){
comment.setUserId(hostHolder.getUser().getId()); //后面做统一异常的处理(这里可能会有未登录的错误)
comment.setStatus(0);
comment.setCreateTime(new Date());
commentService.addComment(comment);
//添加评论后通知
//触发评论事件
Event event = new Event()
.setTopic(TOPIC_COMMENT)
.setUserId(hostHolder.getUser().getId())
.setEntityId(comment.getEntityType())
.setEntityId(comment.getEntityId())
.setData("postId",discussPostId);
if (comment.getEntityType() == ENTITY_TYPE_POST){
DiscussPost target = discussPostService.findDiscussPostById(comment.getEntityId());
event.setEntityUserId(target.getUserId());
}else if (comment.getEntityType() == ENTITY_TYPE_COMMENT){
Comment target = commentService.findCommentById(comment.getEntityId());
event.setEntityUserId(target.getUserId());
}
//设置全了,就发送消息
eventProducer.fireEvent(event);
return "redirect:/discuss/detail/"+discussPostId;
}
这里在Like方法中多增加了一个postId的参数,所以要在前端页面进行修改,多传一个属性进来。记得js文件也要修改
<a href="javascript:;" th:οnclick="|like(this,2,${cvo.comment.id},${cvo.comment.userId},${post.id})
//触发点赞事件
if (likeStatus == 1){
//当前逻辑是点赞,才触发事件
Event event = new Event()
.setTopic(TOPIC_LIKE)
.setEntityType(entityType)
.setEntityId(entityId)
.setEntityUserId(entityUserId)
.setData("postId",postId);
eventProducer.fireEvent(event);
}
//触发通知
Event event = new Event()
.setTopic(TOPIC_FOLLOW)
.setUserId(user.getId())
.setEntityType(entityType)
.setUserId(entityId)
.setEntityUserId(entityId);
eventProducer.fireEvent(event);
最后测试的坑:
//之前我们所有对service的访问请求都是通过controller访问的,
//现在我们使用consumer去调用的service,这次调用里就没有request,就会出现空指针异常
if (attributes == null){
return;
}
HttpServletRequest request = attributes.getRequest();