微信朋友圈的设计实现

目录

基本表结构设计

缓存设计

优化策略

删除、拉黑、标签、不让他看、三天可见该怎么办,进一步改进代码


最近想了一想微信朋友圈的设计,突发想实现一下微信朋友圈的主要代码,代码有错还请指出。

基本表结构设计

-- 1. 用户表
CREATE TABLE `user` (
    `id` bigint NOT NULL AUTO_INCREMENT,
    `nickname` varchar(32) NOT NULL COMMENT '昵称',
    `avatar` varchar(255) DEFAULT NULL COMMENT '头像',
    `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB;

-- 2. 朋友关系表
CREATE TABLE `friendship` (
    `id` bigint NOT NULL AUTO_INCREMENT,
    `user_id` bigint NOT NULL COMMENT '用户ID',
    `friend_id` bigint NOT NULL COMMENT '好友ID',
    `status` tinyint NOT NULL DEFAULT '0' COMMENT '状态:0-正常 1-特别关注 2-屏蔽',
    `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_user_friend` (`user_id`, `friend_id`),
   
) ENGINE=InnoDB;

-- 3. 动态内容表
CREATE TABLE `moment` (
    `id` bigint NOT NULL AUTO_INCREMENT,
    `user_id` bigint NOT NULL COMMENT '发布用户ID',
    `content` text COMMENT '文本内容',
    `type` tinyint NOT NULL DEFAULT '0' COMMENT '类型:0-文本 1-图片 2-视频',
    `visibility` tinyint NOT NULL DEFAULT '0' COMMENT '可见性:0-所有好友 1-指定好友 2-仅自己',
    `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    KEY `idx_user` (`user_id`)
) ENGINE=InnoDB;

-- 4. 动态媒体表
CREATE TABLE `moment_media` (
    `id` bigint NOT NULL AUTO_INCREMENT,
    `moment_id` bigint NOT NULL COMMENT '动态ID',
    `media_url` varchar(255) NOT NULL COMMENT '媒体URL',
    `media_type` tinyint NOT NULL COMMENT '类型:1-图片 2-视频',
    `sort` int NOT NULL DEFAULT '0' COMMENT '排序',
    PRIMARY KEY (`id`),
    KEY `idx_moment_id` (`moment_id`)
) ENGINE=InnoDB;

-- 5. 时间线表
CREATE TABLE `timeline` (
    `id` bigint NOT NULL AUTO_INCREMENT,
    `user_id` bigint NOT NULL COMMENT '接收用户ID',
    `moment_id` bigint NOT NULL COMMENT '动态ID',
    `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_user_moment` (`user_id`, `moment_id`),
    KEY `idx_user_created` (`user_id`, `created_at`)
) ENGINE=InnoDB;

核心业务逻辑实现 

@Service
public class TimelineService {
    
    @Autowired
    private MomentService momentService;
    
    @Autowired
    private FriendshipService friendshipService;
    
    /**
     * 发布动态时推送到好友时间线
     */
    @Transactional
    public void pushMoment(Long momentId, Long userId) {
        // 1. 获取动态信息
        Moment moment = momentService.getMomentById(momentId);
        if (moment == null || !moment.getUserId().equals(userId)) {
            throw new BusinessException("动态不存在或无权操作");
        }
        
        // 2. 获取好友列表
        List<Long> friendIds = friendshipService.getFriendIds(userId);
        
        // 3. 批量插入时间线
        List<Timeline> timelines = new ArrayList<>();
        for (Long friendId : friendIds) {
            // 检查可见性
            if (canSee(moment, friendId)) {
                Timeline timeline = new Timeline();
                timeline.setUserId(friendId);
                timeline.setMomentId(momentId);
                timelines.add(timeline);
            }
        }
        
        if (!timelines.isEmpty()) {
            timelineMapper.batchInsert(timelines);
        }
    }
    
    /**
     * 获取用户时间线
     */
    public PageResult<MomentVO> getTimeline(Long userId, Long lastId, int pageSize) {
        // 1. 分页查询时间线
        List<Timeline> timelines = timelineMapper.queryByUser(userId, lastId, pageSize);
        if (timelines.isEmpty()) {
            return PageResult.empty();
        }
        
        // 2. 批量查询动态详情
        List<Long> momentIds = timelines.stream()
                .map(Timeline::getMomentId)
                .collect(Collectors.toList());
        
        List<Moment> moments = momentService.batchGetMoments(momentIds);
        
        // 3. 组装数据
        return assembleMomentVOs(moments);
    }
}

缓存设计

@Service
public class TimelineCacheService {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    private static final String TIMELINE_KEY = "timeline:%d";
    private static final int CACHE_SIZE = 200;  // 缓存最近200条
    
    /**
     * 将新动态推送到缓存
     */
    public void pushToCache(Long userId, Long momentId) {
        String key = String.format(TIMELINE_KEY, userId);
        redisTemplate.opsForZSet().add(key, momentId.toString(), 
                System.currentTimeMillis());
        
        // 保持缓存大小
        redisTemplate.opsForZSet().removeRange(key, 0, -(CACHE_SIZE + 1));
    }
    
    /**
     * 从缓存获取时间线
     */
    public List<Long> getFromCache(Long userId, long offset, int count) {
        String key = String.format(TIMELINE_KEY, userId);
        Set<String> momentIds = redisTemplate.opsForZSet()
                .reverseRange(key, offset, offset + count - 1);
        
        if (momentIds == null || momentIds.isEmpty()) {
            return Collections.emptyList();
        }
        
        return momentIds.stream()
                .map(Long::parseLong)
                .collect(Collectors.toList());
    }
}

优化策略

@Service
public class TimelineOptimizeService {
    
    /**
     * 异步推送时间线
     */
    @Async
    public void asyncPushTimeline(Long momentId, Long userId) {
        // 使用消息队列异步推送
        timelineProducer.sendMessage(new TimelineMessage(momentId, userId));
    }
    
  
}

删除、拉黑、标签、不让他看、三天可见该怎么办,进一步改进代码

增加表字段

-- 动态表增加字段
ALTER TABLE `moment` ADD COLUMN `expire_time` timestamp NULL COMMENT '过期时间';
ALTER TABLE `moment` ADD COLUMN `status` tinyint NOT NULL DEFAULT '0' COMMENT '状态:0-正常 1-删除';

-- 动态可见性表(控制谁可见/不可见)
CREATE TABLE `moment_visibility` (
    `id` bigint NOT NULL AUTO_INCREMENT,
    `moment_id` bigint NOT NULL COMMENT '动态ID',
    `user_id` bigint NOT NULL COMMENT '用户ID',
    `type` tinyint NOT NULL COMMENT '类型:1-可见 2-不可见',
    `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_moment_user` (`moment_id`, `user_id`),
    KEY `idx_user_id` (`user_id`)
) ENGINE=InnoDB;

-- 用户标签表
CREATE TABLE `user_tag` (
    `id` bigint NOT NULL AUTO_INCREMENT,
    `user_id` bigint NOT NULL COMMENT '创建者ID',
    `name` varchar(32) NOT NULL COMMENT '标签名称',
    `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    KEY `idx_user_id` (`user_id`)
) ENGINE=InnoDB;

-- 标签关系表
CREATE TABLE `user_tag_relation` (
    `id` bigint NOT NULL AUTO_INCREMENT,
    `tag_id` bigint NOT NULL COMMENT '标签ID',
    `target_user_id` bigint NOT NULL COMMENT '被标签的用户ID',
    `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_tag_user` (`tag_id`, `target_user_id`)
) ENGINE=InnoDB;

-- 黑名单表
CREATE TABLE `user_blacklist` (
    `id` bigint NOT NULL AUTO_INCREMENT,
    `user_id` bigint NOT NULL COMMENT '用户ID',
    `blocked_user_id` bigint NOT NULL COMMENT '被拉黑用户ID',
    `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_user_blocked` (`user_id`, `blocked_user_id`)
) ENGINE=InnoDB;

优化设计

@Service
@Transactional
public class MomentService {
    
    /**
     * 发布动态
     */
    public Long publishMoment(MomentDTO dto) {
        Moment moment = new Moment();
        BeanUtils.copyProperties(dto, moment);
        
        // 设置过期时间(如果是三天可见)
        if (dto.isThreeDayVisible()) {
            moment.setExpireTime(LocalDateTime.now().plusDays(3));
        }
        
        momentMapper.insert(moment);
       
        
        // 异步推送到时间线
        timelineService.asyncPushMoment(moment.getId(), dto);
        
        return moment.getId();
    }
    
    /**
     * 删除动态
     */
    public void deleteMoment(Long momentId, Long userId) {
        Moment moment = momentMapper.selectById(momentId);
        if (moment == null || !moment.getUserId().equals(userId)) {
            throw new BusinessException("无权操作");
        }
        
        // 软删除动态
        moment.setStatus(1);
        momentMapper.updateById(moment);
        
        // 清理时间线
        timelineService.removeMomentFromTimeline(momentId);
        // 清理缓存
        momentCacheService.removeMoment(momentId);
    }
    
    /**
     * 获取时间线,需要处理各种可见性规则
     */
    public PageResult<MomentVO> getTimeline(TimelineQuery query) {
        // 1. 获取黑名单用户
        Set<Long> blacklistUserIds = blacklistService.getBlockedUserIds(query.getUserId());
        
        // 2. 构建查询条件
        TimelineQueryBuilder builder = new TimelineQueryBuilder()
            .userId(query.getUserId())
            .excludeUserIds(blacklistUserIds)
            .lastId(query.getLastId())
            .pageSize(query.getPageSize());
            
        // 3. 查询时间线
        List<Timeline> timelines = timelineMapper.queryByCondition(builder.build());
        
        // 4. 过滤处理
        List<MomentVO> moments = filterAndAssembleMoments(timelines, query.getUserId());
        
        return new PageResult<>(moments);
    }
    
    /**
     * 过滤和组装动态
     */
    private List<MomentVO> filterAndAssembleMoments(List<Timeline> timelines, Long userId) {
        List<MomentVO> result = new ArrayList<>();
        
        for (Timeline timeline : timelines) {
            Moment moment = momentMapper.selectById(timeline.getMomentId());
            if (moment == null || moment.getStatus() == 1) {
                continue;
            }
            
            // 检查过期时间
            if (moment.getExpireTime() != null && 
                moment.getExpireTime().isBefore(LocalDateTime.now())) {
                continue;
            }
            
            // 检查可见性
            if (!checkVisibility(moment, userId)) {
                continue;
            }
            
            MomentVO vo = assembleMomentVO(moment);
            result.add(vo);
        }
        
        return result;
    }
    
    /**
     * 检查动态可见性
     */
    private boolean checkVisibility(Moment moment, Long userId) {
        // 1. 检查是否在黑名单中
        if (blacklistService.isBlocked(moment.getUserId(), userId)) {
            return false;
        }
        
        // 2. 检查可见性设置
        List<MomentVisibility> visibilities = 
            visibilityMapper.getByMomentId(moment.getId());
        
        // 处理可见性规则
        boolean hasVisibleList = false;
        boolean hasInvisibleList = false;
        
        for (MomentVisibility visibility : visibilities) {
            if (visibility.getType() == 1) {
                hasVisibleList = true;
                if (visibility.getUserId().equals(userId)) {
                    return true;
                }
            } else if (visibility.getType() == 2) {
                hasInvisibleList = true;
                if (visibility.getUserId().equals(userId)) {
                    return false;
                }
            }
        }
        
        // 如果有可见列表,不在列表中则不可见
        if (hasVisibleList) {
            return false;
        }
        
        // 如果只有不可见列表,不在列表中则可见
        return true;
    }
}

@Service
public class BlacklistService {
    
    /**
     * 拉黑用户
     */
    @Transactional
    public void blockUser(Long userId, Long blockedUserId) {
        // 1. 添加黑名单记录
        UserBlacklist blacklist = new UserBlacklist();
        blacklist.setUserId(userId);
        blacklist.setBlockedUserId(blockedUserId);
        blacklistMapper.insert(blacklist);
        
        // 2. 删除相关动态从时间线
        timelineService.removeUserMomentsFromTimeline(userId, blockedUserId);
        
        // 3. 清理缓存
        blacklistCacheService.refreshUserBlacklist(userId);
    }
}

@Service
public class UserTagService {
    
    /**
     * 创建标签并添加用户
     */
    @Transactional
    public void createTagAndAddUsers(TagDTO dto) {
        // 1. 创建标签
        UserTag tag = new UserTag();
        tag.setUserId(dto.getUserId());
        tag.setName(dto.getName());
        tagMapper.insert(tag);
        
        // 2. 添加用户关系
        if (dto.getTargetUserIds() != null) {
            for (Long targetUserId : dto.getTargetUserIds()) {
                UserTagRelation relation = new UserTagRelation();
                relation.setTagId(tag.getId());
                relation.setTargetUserId(targetUserId);
                tagRelationMapper.insert(relation);
            }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值