牛客网后端项目实战(三十三):发送系统通知

• 触发事件

  • 评论后,发布通知
  • 点赞后,发布通知
  • 关注后,发布通知

• 处理事件

  • 封装事件对象
  • 开发事件的生产者
  • 开发事件的消费者

发送系统通知

在这里插入图片描述
评论,点赞、关注等系统通知是 并发、异步的。可以将其抽象为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, "已关注!");
    }
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值