• 触发事件
- 评论后,发布通知
- 点赞后,发布通知
- 关注后,发布通知
• 处理事件
- 封装事件对象
- 开发事件的生产者
- 开发事件的消费者
发送系统通知
评论,点赞、关注等系统通知是 并发、异步的。可以将其抽象为event时间(Entity类转化为event类,然后让Kafka处理)
定义Event实体
package com.nowcoder.community.entity;
import java.util.HashMap;
import java.util.Map;
public class Event {
private String topic; \\ 表示event类型(评论\点赞\关注)
private int userId; \\ 表示执行event的用户id
private int entityType; \\ entityType 与 entityId 确定了该topic 的Entity对象(帖子\回复\用户)
private int entityId;
private int entityUserId; \\ Entity对象的useId
private Map<String, Object> data = new HashMap<>(); \\ map 用来存放一些补充数据,比如时间
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;
}
}
1、带有返回Event的 setXXX() 方法,可以在实例化的时候更灵活点
public Event setTopic(String topic) {
this.topic = topic;
return this;
}
新建生产者/消费者线程
新建event包
生产者
package com.nowcoder.community.event;
import com.alibaba.fastjson.JSONObject;
import com.nowcoder.community.entity.Event;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Component;
@Component
public class EventProducer {
@Autowired
private KafkaTemplate kafkaTemplate;
// 处理事件
public void fireEvent(Event event) {
// 将事件发布到指定的主题
kafkaTemplate.send(event.getTopic(), JSONObject.toJSONString(event));
}
}
1、注入 KafkaTemplate 调用kafkaTemplate.send即可
2、event.getTopic() 得到topic;JSONObject.toJSONString(event)) 将event转化成json字符串直接作为context
消费者
其实就是把Event类转换成Message类,然后存入message表中
@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}) // 该方法监听3个topic
public void handleCommentMessage(ConsumerRecord record) {
if (record == null || record.value() == null) {
logger.error("消息的内容为空!");
return;
}
Event event = JSONObject.parseObject(record.value().toString(), Event.class); // json字符串解析为 Event实例
if (event == null) {
logger.error("消息格式错误!");
return;
}
// 发送站内通知
// 系统发送“ userId +关注/点赞/评论event.getTopic()+ XXX,+链接”
Message message = new Message();
message.setFromId(SYSTEM_USER_ID);
message.setToId(event.getEntityUserId());
message.setConversationId(event.getTopic()); // 直接将topic作为ConversationId
message.setCreateTime(new Date());
// 剩下的一些数据放在map,生成josn字符串放在 message.setContent
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());
}
}
message.setContent(JSONObject.toJSONString(content));
messageService.addMessage(message);
}
}
public interface CommunityConstant {
/**
* 主题: 评论
*/
String TOPIC_COMMENT = "comment";
/**
* 主题: 点赞
*/
String TOPIC_LIKE = "like";
/**
* 主题: 关注
*/
String TOPIC_FOLLOW = "follow";
/**
* 系统用户ID
*/
int SYSTEM_USER_ID = 1;
}
1、封装生产者和消费者。
生产者要主动调用 kafkaTemplate.send(topic, content) 方法;消费者通过注解,监听某topic,然后进行处理
2、测试方法中调用生产者就行,消费者是被动触发的。
设置触发事件
1、添加评论时,触发一次生产者
// 对某个帖子进行评论,get请求
@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()) // 谁触发的event
.setEntityType(comment.getEntityType()) // 评论的Entity
.setEntityId(comment.getEntityId())
.setData("postId", discussPostId); // 被评论的帖子id
// EntityUserId要查不同的表
// ENTITY_TYPE_POST 说对帖子评论;ENTITY_TYPE_COMMENT 表示对评论回复
if (comment.getEntityType() == ENTITY_TYPE_POST) {
DiscussPost target = discussPostService.findDiscussPostById(comment.getEntityId());
event.setEntityUserId(target.getUserId());
} else if (comment.getEntityType() == ENTITY_TYPE_COMMENT) {
Comment tLikearget = commentService.findCommentById(comment.getEntityId());
event.setEntityUserId(target.getUserId());
}
eventProducer.fireEvent(event);
return "redirect:/discuss/detail/" + discussPostId;
}
1、设置Event
2、调用eventProducer.fireEvent(event);
2、点赞触发event
// LikeController
@RequestMapping(path = "/like", method = RequestMethod.POST)
@ResponseBody
public String like(int entityType, int entityId, int entityUserId, int postId) {
User user = hostHolder.getUser();
// 点赞
likeService.like(user.getId(), entityType, entityId, entityUserId);
// 数量
long likeCount = likeService.findEntityLikeCount(entityType, entityId);
// 状态
int likeStatus = likeService.findEntityLikeStatus(user.getId(), entityType, entityId);
// 返回的结果
Map<String, Object> map = new HashMap<>();
map.put("likeCount", likeCount);
map.put("likeStatus", likeStatus);
// 触发点赞事件
if (likeStatus == 1) {
Event event = new Event()
.setTopic(TOPIC_LIKE)
.setUserId(hostHolder.getUser().getId())
.setEntityType(entityType)
.setEntityId(entityId)
.setEntityUserId(entityUserId)
.setData("postId", postId);
eventProducer.fireEvent(event);
}
return CommunityUtil.getJSONString(0, null, map);
}
3、关注触发event
// FollowController
@RequestMapping(path = "/follow", method = RequestMethod.POST)
@ResponseBody
public String follow(int entityType, int entityId) {
User user = hostHolder.getUser();
followService.follow(user.getId(), entityType, entityId);
// 触发关注事件
Event event = new Event()
.setTopic(TOPIC_FOLLOW)
.setUserId(hostHolder.getUser().getId())
.setEntityType(entityType)
.setEntityId(entityId)
.setEntityUserId(entityId);
eventProducer.fireEvent(event);
return CommunityUtil.getJSONString(0, "已关注!");
}