目录
删除、拉黑、标签、不让他看、三天可见该怎么办,进一步改进代码
最近想了一想微信朋友圈的设计,突发想实现一下微信朋友圈的主要代码,代码有错还请指出。
基本表结构设计
-- 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);
}
}
}
}