Skip to main content

消息点赞点踩策略模式


消息点赞点踩策略模式

我们可以对某个人发送的消息进行点赞和点踩

需要注意的是,一开始我们给一个消息点赞,此时这个人的点赞数量是1

如果这时候,我们选择点踩,那么应该先取消原来的点赞,然后再进行点踩操作,这时候点赞数量就是-1

这个过程如果用大量的ifelse写就不是很优雅

if(点赞){
	if(点过了踩){
    //取消点踩
  }
}else if(点踩){
	if(点过了赞){
    //取消点赞
  }
}

消除if-else最常见的手段就是使用策略模式。

image-20240529213050580

在点赞或者点踩的时候,根据不同的type加载不同的策略

@Override
public void setMsgMark(Long uid, ChatMessageMarkReq request) {
    AbstractMsgMarkStrategy strategy = MsgMarkFactory.getStrategyNoNull(request.getMarkType());
    switch (MessageMarkActTypeEnum.of(request.getActType())) {
        case MARK:
            strategy.mark(uid, request.getMsgId());
            break;
        case UN_MARK:
            strategy.unMark(uid, request.getMsgId());
            break;
    }
}

点赞策略:

点赞的同时,如果之前有点踩,需要取消点踩的动作

@Component
public class LikeStrategy extends AbstractMsgMarkStrategy {

    @Override
    protected MessageMarkTypeEnum getTypeEnum() {
        return MessageMarkTypeEnum.LIKE;
    }

    @Override
    public void doMark(Long uid, Long msgId) {
        super.doMark(uid, msgId);
        //同时取消点踩的动作
        MsgMarkFactory.getStrategyNoNull(MessageMarkTypeEnum.DISLIKE.getType()).unMark(uid, msgId);
    }
}

具体的实现:

protected void exec(Long uid, Long msgId, MessageMarkActTypeEnum actTypeEnum) {
    Integer markType = getTypeEnum().getType();
    Integer actType = actTypeEnum.getType();
    MessageMark oldMark = messageMarkService.get(uid, msgId, markType);
    if (Objects.isNull(oldMark) && actTypeEnum == MessageMarkActTypeEnum.UN_MARK) {
        //取消的类型,数据库一定有记录,没有就直接跳过操作
        return;
    }
    //插入一条新消息,或者修改一条消息
    MessageMark insertOrUpdate = MessageMark.builder()
            .id(Optional.ofNullable(oldMark).map(MessageMark::getId).orElse(null))
            .uid(uid)
            .msgId(msgId)
            .type(markType)
            .status(transformAct(actType))
            .build();
    boolean modify = messageMarkService.saveOrUpdate(insertOrUpdate);
    if (modify) {
        //修改成功才发布消息标记事件
        ChatMessageMarkDTO dto = new ChatMessageMarkDTO(uid, msgId, markType, actType);
        applicationEventPublisher.publishEvent(new MessageMarkEvent(this, dto));
    }
}

我们还会有一个监听器,因为如果用户的消息被点赞超过10个 了,我们会给这个用户发放一个勋章

/**
 * 消息标记监听器
 */
@Slf4j
@Component
public class MessageMarkListener {
    @Async
    @TransactionalEventListener(classes = MessageMarkEvent.class, fallbackExecution = true)
    public void changeMsgType(MessageMarkEvent event) throws InterruptedException {
        ChatMessageMarkDTO dto = event.getDto();
        Message msg = messageService.getById(dto.getMsgId());
        if (!Objects.equals(msg.getType(), MessageTypeEnum.TEXT.getType())) {// 普通消息才需要升级
            return;
        }
        // 消息被标记次数
        Integer markCount = messageMarkService.getMarkCount(dto.getMsgId(), dto.getMarkType());
        MessageMarkTypeEnum markTypeEnum = MessageMarkTypeEnum.of(dto.getMarkType());
        if (markCount < markTypeEnum.getRiseNum()) {
            return;
        }
        if (MessageMarkTypeEnum.LIKE.getType().equals(dto.getMarkType())) {// 尝试给用户发送一张徽章
            userBackpackService.acquireItem(msg.getFromUid(), ItemEnum.LIKE_BADGE.getId(), IdempotentEnum.MSG_ID, msg.getId().toString());
        }
    }

    @Async
    @TransactionalEventListener(classes = MessageMarkEvent.class, fallbackExecution = true)
    public void notifyAll(MessageMarkEvent event) {// 后续可做合并查询,目前异步影响不大
        ChatMessageMarkDTO dto = event.getDto();
        Integer markCount = messageMarkService.getMarkCount(dto.getMsgId(), dto.getMarkType());
        pushService.sendPushMsg(WSAdapter.buildMsgMarkSend(dto, markCount));
    }

}