package cn.iocoder.yudao.module.im.dal.mysql.conversation; import cn.hutool.core.date.LocalDateTimeUtil; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.im.dal.dataobject.conversation.ImConversationReadDO; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import org.apache.ibatis.annotations.Mapper; import java.time.LocalDateTime; import java.util.Collection; import java.util.List; /** * IM 会话读位置 Mapper * * @author 芋道源码 */ @Mapper public interface ImConversationReadMapper extends BaseMapperX { default ImConversationReadDO selectByUserIdAndConversation(Long userId, Integer conversationType, Long conversationId) { return selectOne(new LambdaQueryWrapperX() .eq(ImConversationReadDO::getUserId, userId) .eq(ImConversationReadDO::getConversationType, conversationType) .eq(ImConversationReadDO::getTargetId, conversationId)); } default List selectListByConversation(Integer conversationType, Long conversationId) { return selectList(new LambdaQueryWrapperX() .eq(ImConversationReadDO::getConversationType, conversationType) .eq(ImConversationReadDO::getTargetId, conversationId)); } default List selectListByUserIdAndConversations(Long userId, Integer conversationType, Collection conversationIds) { return selectList(new LambdaQueryWrapperX() .eq(ImConversationReadDO::getUserId, userId) .eq(ImConversationReadDO::getConversationType, conversationType) .in(ImConversationReadDO::getTargetId, conversationIds)); } /** * 增量拉取当前用户的会话读位置(按 update_time + id 正向游标) * * @param userId 当前用户编号 * @param lastUpdateTime 上次拉取到的更新时间;首次拉取传 null * @param lastId 上次拉取到的记录编号;首次拉取传 null * @param limit 拉取数量 * @return 会话读位置列表 */ default List selectPullListByUserId(Long userId, Long lastUpdateTime, Long lastId, Integer limit) { LambdaQueryWrapperX query = new LambdaQueryWrapperX() .eq(ImConversationReadDO::getUserId, userId); if (lastUpdateTime != null && lastId != null) { LocalDateTime lastTime = LocalDateTimeUtil.of(lastUpdateTime); query.and(w -> w.gt(ImConversationReadDO::getUpdateTime, lastTime) .or(n -> n.eq(ImConversationReadDO::getUpdateTime, lastTime).gt(ImConversationReadDO::getId, lastId))); } return selectList(query.orderByAsc(ImConversationReadDO::getUpdateTime).orderByAsc(ImConversationReadDO::getId) .last("LIMIT " + limit)); } /** * 单调递增更新读位置:仅当新位置更大时才更新 *

* 通过 {@code read_message_id < messageId} 的 CAS 条件,保证乱序 / 并发上报时读位置不会回退。 * * @param id 记录编号 * @param messageId 新的最大已读消息编号 * @param readTime 已读时间 * @return 影响行数;0 表示新位置不大于已有位置,未更新 */ default int updateReadMessageIdToLarger(Long id, Long messageId, LocalDateTime readTime) { return update(null, Wrappers.lambdaUpdate() .set(ImConversationReadDO::getMessageId, messageId) .set(ImConversationReadDO::getReadTime, readTime) .set(ImConversationReadDO::getUpdateTime, readTime) .eq(ImConversationReadDO::getId, id) .lt(ImConversationReadDO::getMessageId, messageId)); } }