当前位置: 首页 > article >正文

Java全栈项目实战:校园论坛社交平台开发

项目简介

本文将分享一个基于 Spring Boot + Vue.js 开发的校园论坛社交平台项目的开发经验。该平台旨在为校园师生提供一个交流互动的线上空间,支持发帖、评论、私信等核心社交功能。

技术栈

后端

  • Spring Boot 2.x
  • Spring Security
  • MyBatis Plus
  • Redis
  • MySQL 8.0
  • ElasticSearch

前端

  • Vue.js 2.x
  • Element UI
  • Axios
  • Vuex
  • Vue Router

核心功能模块

1. 用户管理

  • 注册/登录
  • 个人信息管理
  • 头像上传
  • 权限控制

2. 帖子管理

  • 发布帖子
  • 帖子分类
  • 帖子点赞/收藏
  • 富文本编辑器
  • 图片上传

3. 评论系统

  • 发表评论
  • 回复评论
  • 评论点赞
  • 楼中楼回复

4. 私信系统

  • 发送私信
  • 私信列表
  • 未读消息提醒
  • 实时通知

5. 搜索功能

  • 全文检索
  • 热门话题
  • 智能推荐

技术难点解决方案

1. 性能优化

// 使用 Redis 缓存热门帖子
@Cacheable(value = "hot_posts", key = "#page")
public List<Post> getHotPosts(int page) {
    // 从缓存获取数据
    // 如果缓存未命中则查询数据库
}

2. 消息队列应用

// 使用 RabbitMQ 处理点赞消息
@RabbitListener(queues = "like.queue")
public void handleLikeMessage(LikeMessage message) {
    // 异步处理点赞逻辑
    likeService.processLike(message);
}

3. 分布式会话管理

// 使用 Redis 存储会话信息
@Configuration
public class SessionConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        // Redis 配置
    }
}

项目亮点

  1. 高并发处理

    • 使用 Redis 缓存热点数据
    • 采用分布式锁处理并发请求
    • 实现请求限流保护
  2. 实时通讯

    • WebSocket 实现即时消息推送
    • 心跳检测保持连接稳定
    • 离线消息处理机制
  3. 搜索优化

    • ElasticSearch 全文检索
    • 分词器优化
    • 搜索结果权重排序

项目部署

1. 容器化部署

version: '3'
services:
  backend:
    image: campus-forum-backend
    ports:
      - "8080:8080"
    depends_on:
      - mysql
      - redis
      
  frontend:
    image: campus-forum-frontend
    ports:
      - "80:80"

2. 性能监控

  • 使用 Spring Boot Admin 监控应用
  • 集成 Prometheus + Grafana 监控系统指标
  • 接入 ELK 日志分析平台

开发心得

  1. 技术选型

    • 选择稳定可靠的主流技术栈
    • 考虑团队技术水平和学习成本
    • 预留技术升级空间
  2. 项目管理

    • 采用 Git Flow 工作流
    • 制定代码规范
    • 持续集成/持续部署
  3. 用户体验

    • 注重页面交互设计
    • 优化加载速度
    • 完善错误处理

后续优化方向

  1. 引入微服务架构
  2. 优化移动端适配
  3. 增加更多社交功能
  4. 提升安全性能
  5. 优化推荐算法

总结

通过这个项目,不仅实践了全栈开发技能,也深入理解了分布式系统设计、性能优化等重要概念。项目开发过程中遇到的问题和解决方案,都为今后的开发工作提供了宝贵经验。

Java全栈项目:校园论坛社交平台核心功能详细设计

一、用户管理模块

1. 用户注册与登录

1.1 数据库设计
CREATE TABLE `user` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `username` varchar(50) NOT NULL COMMENT '用户名',
  `password` varchar(128) NOT NULL COMMENT '密码',
  `email` varchar(128) NOT NULL COMMENT '邮箱',
  `avatar` varchar(255) DEFAULT NULL COMMENT '头像URL',
  `status` tinyint DEFAULT '1' COMMENT '状态:0-禁用 1-正常',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_username` (`username`),
  UNIQUE KEY `uk_email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
1.2 注册流程
@Service
public class UserService {
    
    @Autowired
    private PasswordEncoder passwordEncoder;
    
    public void register(UserRegisterDTO dto) {
        // 1. 验证用户名和邮箱是否已存在
        validateUserInfo(dto);
        
        // 2. 密码加密
        String encodedPassword = passwordEncoder.encode(dto.getPassword());
        
        // 3. 保存用户信息
        User user = new User();
        user.setUsername(dto.getUsername());
        user.setPassword(encodedPassword);
        user.setEmail(dto.getEmail());
        
        // 4. 发送验证邮件
        sendVerificationEmail(dto.getEmail());
        
        userMapper.insert(user);
    }
}
1.3 登录认证
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
            .loginProcessingUrl("/api/login")
            .successHandler(new LoginSuccessHandler())
            .failureHandler(new LoginFailureHandler())
            .and()
            .csrf().disable();
    }
}

2. 个人信息管理

2.1 信息更新接口
@RestController
@RequestMapping("/api/user")
public class UserController {
    
    @PutMapping("/profile")
    public Result updateProfile(@RequestBody UserProfileDTO dto) {
        // 更新用户信息
        userService.updateProfile(dto);
        return Result.success();
    }
    
    @PutMapping("/password")
    public Result updatePassword(@RequestBody PasswordUpdateDTO dto) {
        // 修改密码
        userService.updatePassword(dto);
        return Result.success();
    }
}
2.2 头像上传
@Service
public class FileService {
    
    @Value("${upload.path}")
    private String uploadPath;
    
    public String uploadAvatar(MultipartFile file) {
        // 1. 校验文件类型和大小
        validateImage(file);
        
        // 2. 生成文件名
        String fileName = generateFileName(file);
        
        // 3. 保存文件
        String filePath = uploadPath + fileName;
        file.transferTo(new File(filePath));
        
        // 4. 返回访问URL
        return getAccessUrl(fileName);
    }
}

3. 权限控制

3.1 RBAC模型设计
-- 角色表
CREATE TABLE `role` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL COMMENT '角色名称',
  `code` varchar(50) NOT NULL COMMENT '角色编码',
  PRIMARY KEY (`id`)
);

-- 权限表
CREATE TABLE `permission` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL COMMENT '权限名称',
  `code` varchar(50) NOT NULL COMMENT '权限编码',
  PRIMARY KEY (`id`)
);

-- 用户角色关联表
CREATE TABLE `user_role` (
  `user_id` bigint NOT NULL,
  `role_id` bigint NOT NULL,
  PRIMARY KEY (`user_id`, `role_id`)
);
3.2 权限注解
@PreAuthorize("hasRole('ADMIN')")
@GetMapping("/admin/users")
public Result listUsers() {
    return Result.success(userService.listUsers());
}

@PreAuthorize("hasPermission(#id, 'POST', 'DELETE')")
@DeleteMapping("/posts/{id}")
public Result deletePost(@PathVariable Long id) {
    postService.deletePost(id);
    return Result.success();
}

二、帖子管理模块

1. 帖子发布

1.1 数据库设计
CREATE TABLE `post` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `user_id` bigint NOT NULL COMMENT '发帖人ID',
  `category_id` bigint NOT NULL COMMENT '分类ID',
  `title` varchar(255) NOT NULL COMMENT '标题',
  `content` text NOT NULL COMMENT '内容',
  `status` tinyint DEFAULT '1' COMMENT '状态:0-删除 1-正常',
  `view_count` int DEFAULT '0' COMMENT '浏览量',
  `like_count` int DEFAULT '0' COMMENT '点赞数',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idx_user_id` (`user_id`),
  KEY `idx_category_id` (`category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
1.2 发帖接口
@RestController
@RequestMapping("/api/posts")
public class PostController {
    
    @PostMapping
    public Result createPost(@RequestBody PostCreateDTO dto) {
        // 1. 参数校验
        validatePost(dto);
        
        // 2. 处理富文本内容
        String content = processRichText(dto.getContent());
        
        // 3. 保存帖子
        Post post = new Post();
        post.setTitle(dto.getTitle());
        post.setContent(content);
        post.setCategoryId(dto.getCategoryId());
        post.setUserId(getCurrentUserId());
        
        postService.createPost(post);
        
        return Result.success();
    }
}

2. 帖子分类管理

2.1 分类树形结构
@Data
public class CategoryTree {
    private Long id;
    private String name;
    private List<CategoryTree> children;
    
    public static List<CategoryTree> buildTree(List<Category> categories) {
        // 构建树形结构
        Map<Long, CategoryTree> nodeMap = new HashMap<>();
        List<CategoryTree> roots = new ArrayList<>();
        
        // 构建节点映射
        categories.forEach(category -> {
            CategoryTree node = new CategoryTree();
            node.setId(category.getId());
            node.setName(category.getName());
            nodeMap.put(category.getId(), node);
        });
        
        // 构建树形关系
        categories.forEach(category -> {
            if (category.getParentId() == 0) {
                roots.add(nodeMap.get(category.getId()));
            } else {
                CategoryTree parent = nodeMap.get(category.getParentId());
                if (parent != null) {
                    if (parent.getChildren() == null) {
                        parent.setChildren(new ArrayList<>());
                    }
                    parent.getChildren().add(nodeMap.get(category.getId()));
                }
            }
        });
        
        return roots;
    }
}

3. 点赞/收藏功能

3.1 Redis实现点赞
@Service
public class LikeService {
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    private static final String LIKE_KEY = "post:like:";
    
    public void like(Long postId, Long userId) {
        String key = LIKE_KEY + postId;
        
        // 判断是否已点赞
        Boolean isMember = redisTemplate.opsForSet().isMember(key, userId.toString());
        
        if (Boolean.TRUE.equals(isMember)) {
            // 取消点赞
            redisTemplate.opsForSet().remove(key, userId.toString());
            // 点赞数-1
            redisTemplate.opsForValue().decrement(LIKE_COUNT_KEY + postId);
        } else {
            // 点赞
            redisTemplate.opsForSet().add(key, userId.toString());
            // 点赞数+1
            redisTemplate.opsForValue().increment(LIKE_COUNT_KEY + postId);
        }
    }
}

三、评论系统

1. 评论数据结构

1.1 数据库设计
CREATE TABLE `comment` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `post_id` bigint NOT NULL COMMENT '帖子ID',
  `user_id` bigint NOT NULL COMMENT '评论人ID',
  `content` text NOT NULL COMMENT '评论内容',
  `parent_id` bigint DEFAULT NULL COMMENT '父评论ID',
  `reply_user_id` bigint DEFAULT NULL COMMENT '回复用户ID',
  `like_count` int DEFAULT '0' COMMENT '点赞数',
  `status` tinyint DEFAULT '1' COMMENT '状态:0-删除 1-正常',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idx_post_id` (`post_id`),
  KEY `idx_parent_id` (`parent_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

2. 评论树形结构

2.1 评论树构建
@Data
public class CommentTreeNode {
    private Long id;
    private String content;
    private UserInfo user;
    private UserInfo replyUser;
    private Date createTime;
    private Integer likeCount;
    private List<CommentTreeNode> children;
    
    public static List<CommentTreeNode> buildTree(List<Comment> comments) {
        // 构建评论树
        Map<Long, CommentTreeNode> nodeMap = new HashMap<>();
        List<CommentTreeNode> roots = new ArrayList<>();
        
        // 转换评论对象
        comments.forEach(comment -> {
            CommentTreeNode node = convertToNode(comment);
            nodeMap.put(comment.getId(), node);
            
            if (comment.getParentId() == null) {
                roots.add(node);
            } else {
                CommentTreeNode parent = nodeMap.get(comment.getParentId());
                if (parent != null) {
                    if (parent.getChildren() == null) {
                        parent.setChildren(new ArrayList<>());
                    }
                    parent.getChildren().add(node);
                }
            }
        });
        
        return roots;
    }
}

3. 评论分页查询

3.1 分页接口
@Service
public class CommentService {
    
    public PageResult<CommentVO> pageComments(Long postId, Integer pageNum, Integer pageSize) {
        // 1. 查询一级评论
        Page<Comment> page = new Page<>(pageNum, pageSize);
        LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<Comment>()
            .eq(Comment::getPostId, postId)
            .isNull(Comment::getParentId)
            .orderByDesc(Comment::getCreateTime);
            
        Page<Comment> commentPage = commentMapper.selectPage(page, wrapper);
        
        // 2. 查询子评论
        List<Comment> comments = commentPage.getRecords();
        if (!comments.isEmpty()) {
            List<Long> parentIds = comments.stream()
                .map(Comment::getId)
                .collect(Collectors.toList());
                
            List<Comment> children = commentMapper.selectList(
                new LambdaQueryWrapper<Comment>()
                    .in(Comment::getParentId, parentIds)
                    .orderByAsc(Comment::getCreateTime)
            );
            
            // 3. 构建评论树
            List<CommentTreeNode> trees = CommentTreeNode.buildTree(
                Stream.concat(comments.stream(), children.stream())
                    .collect(Collectors.toList())
            );
            
            // 4. 转换为VO
            List<CommentVO> vos = convertToVO(trees);
            
            return new PageResult<>(vos, commentPage.getTotal());
        }
        
        return new PageResult<>(Collections.emptyList(), 0L);
    }
}

4. 评论点赞

4.1 点赞实现
@Service
public class CommentLikeService {
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    private static final String COMMENT_LIKE_KEY = "comment:like:";
    
    @Transactional
    public void likeComment(Long commentId, Long userId) {
        String key = COMMENT_LIKE_KEY + commentId;
        
        // 1. 检查是否已点赞
        Boolean hasLiked = redisTemplate.opsForSet().isMember(key, userId.toString());
        
        if (Boolean.TRUE.equals(hasLiked)) {
            // 2. 取消点赞
            redisTemplate.opsForSet().remove(key, userId.toString());
            // 3. 更新点赞数
            commentMapper.decrementLikeCount(commentId);
        } else {
            // 4. 添加点赞
            redisTemplate.opsForSet().add(key, userId.toString());
            // 5. 更新点赞数
            commentMapper.incrementLikeCount(commentId);
            // 6. 发送点赞通知
            sendLikeNotification(commentId, userId);
        }
    }
}

以上是校园论坛社交平台核心模块的详细设计和实现。每个模块都包含了数据库设计、核心业务逻辑实现、性能优化考虑等方面。实际开发中还需要注意:

  1. 数据安全性:

    • 敏感数据加密
    • XSS防护
    • SQL注入防护
    • CSRF防护
  2. 性能优化:

    • 合理使用缓存
    • 索引优化
    • 大数据量分页优化
  3. 可扩展性:

    • 模块解耦
    • 统一异常处理
    • 统一返回格式
    • 参数校验
  4. 用户体验:

    • 防重复提交
    • 友好的错误提示
    • 异步处理耗时操作

这些都是实际项目中需要重点考虑的方面。

Java全栈项目:校园论坛私信系统与搜索功能详解

一、私信系统

1. 数据库设计

1.1 私信表设计
-- 私信会话表
CREATE TABLE `message_conversation` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `sender_id` bigint NOT NULL COMMENT '发送者ID',
  `receiver_id` bigint NOT NULL COMMENT '接收者ID',
  `last_message` varchar(255) DEFAULT NULL COMMENT '最后一条消息',
  `unread_count` int DEFAULT '0' COMMENT '未读消息数',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_sender_receiver` (`sender_id`,`receiver_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 私信内容表
CREATE TABLE `message_content` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `conversation_id` bigint NOT NULL COMMENT '会话ID',
  `sender_id` bigint NOT NULL COMMENT '发送者ID',
  `content` text NOT NULL COMMENT '消息内容',
  `type` tinyint DEFAULT '0' COMMENT '消息类型:0-文本 1-图片 2-文件',
  `status` tinyint DEFAULT '0' COMMENT '状态:0-未读 1-已读',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idx_conversation_id` (`conversation_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

2. WebSocket实时通讯

2.1 WebSocket配置
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(messageHandler(), "/ws/message")
               .setAllowedOrigins("*")
               .addInterceptors(new WebSocketInterceptor());
    }

    @Bean
    public WebSocketHandler messageHandler() {
        return new MessageWebSocketHandler();
    }
}
2.2 消息处理器
@Component
public class MessageWebSocketHandler extends TextWebSocketHandler {
    
    private static final Map<Long, WebSocketSession> USER_SESSIONS = new ConcurrentHashMap<>();

    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
        Long userId = (Long) session.getAttributes().get("userId");
        USER_SESSIONS.put(userId, session);
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) {
        // 处理接收到的消息
        MessageDTO messageDTO = JSON.parseObject(message.getPayload(), MessageDTO.class);
        messageService.sendMessage(messageDTO);
        
        // 推送消息给接收者
        WebSocketSession receiverSession = USER_SESSIONS.get(messageDTO.getReceiverId());
        if (receiverSession != null && receiverSession.isOpen()) {
            receiverSession.sendMessage(new TextMessage(JSON.toJSONString(messageDTO)));
        }
    }
}

3. 消息服务实现

3.1 发送私信
@Service
public class MessageService {
    
    @Autowired
    private MessageMapper messageMapper;
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Transactional
    public void sendMessage(MessageDTO dto) {
        // 1. 获取或创建会话
        MessageConversation conversation = getOrCreateConversation(
            dto.getSenderId(), 
            dto.getReceiverId()
        );
        
        // 2. 保存消息内容
        MessageContent content = new MessageContent();
        content.setConversationId(conversation.getId());
        content.setSenderId(dto.getSenderId());
        content.setContent(dto.getContent());
        content.setType(dto.getType());
        messageMapper.insertContent(content);
        
        // 3. 更新会话信息
        conversation.setLastMessage(dto.getContent());
        conversation.setUnreadCount(conversation.getUnreadCount() + 1);
        messageMapper.updateConversation(conversation);
        
        // 4. 发送未读消息提醒
        sendUnreadNotification(dto.getReceiverId());
    }
}
3.2 私信列表查询
@Service
public class MessageService {
    
    public PageResult<ConversationVO> listConversations(Long userId, Integer pageNum, Integer pageSize) {
        // 1. 分页查询会话列表
        Page<MessageConversation> page = new Page<>(pageNum, pageSize);
        LambdaQueryWrapper<MessageConversation> wrapper = new LambdaQueryWrapper<MessageConversation>()
            .and(w -> w.eq(MessageConversation::getSenderId, userId)
                      .or()
                      .eq(MessageConversation::getReceiverId, userId))
            .orderByDesc(MessageConversation::getUpdateTime);
            
        Page<MessageConversation> conversationPage = messageMapper.selectPage(page, wrapper);
        
        // 2. 转换为VO对象
        List<ConversationVO> vos = conversationPage.getRecords().stream()
            .map(this::convertToVO)
            .collect(Collectors.toList());
            
        return new PageResult<>(vos, conversationPage.getTotal());
    }
    
    public List<MessageContentVO> listMessages(Long conversationId, Long userId) {
        // 1. 查询消息内容
        List<MessageContent> contents = messageMapper.selectList(
            new LambdaQueryWrapper<MessageContent>()
                .eq(MessageContent::getConversationId, conversationId)
                .orderByAsc(MessageContent::getCreateTime)
        );
        
        // 2. 更新未读状态
        messageMapper.updateMessageStatus(conversationId, userId);
        
        // 3. 转换为VO对象
        return contents.stream()
            .map(this::convertToContentVO)
            .collect(Collectors.toList());
    }
}

4. 未读消息提醒

4.1 消息提醒实现
@Service
public class NotificationService {
    
    private static final String UNREAD_COUNT_KEY = "unread:count:";
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    public void sendUnreadNotification(Long userId) {
        // 1. 增加未读消息计数
        String key = UNREAD_COUNT_KEY + userId;
        redisTemplate.opsForValue().increment(key);
        
        // 2. 推送未读提醒
        WebSocketSession session = MessageWebSocketHandler.USER_SESSIONS.get(userId);
        if (session != null && session.isOpen()) {
            NotificationDTO notification = new NotificationDTO();
            notification.setType("unread");
            notification.setCount(getUnreadCount(userId));
            session.sendMessage(new TextMessage(JSON.toJSONString(notification)));
        }
    }
    
    public Integer getUnreadCount(Long userId) {
        String key = UNREAD_COUNT_KEY + userId;
        Object count = redisTemplate.opsForValue().get(key);
        return count == null ? 0 : (Integer) count;
    }
}

二、搜索功能

1. ElasticSearch配置

1.1 索引配置
@Configuration
public class ElasticsearchConfig {
    
    @Bean
    public RestHighLevelClient elasticsearchClient() {
        return new RestHighLevelClient(
            RestClient.builder(new HttpHost("localhost", 9200, "http"))
        );
    }
    
    // 创建帖子索引
    public void createPostIndex() {
        CreateIndexRequest request = new CreateIndexRequest("posts");
        request.mapping(
            "{\n" +
            "  \"properties\": {\n" +
            "    \"title\": {\n" +
            "      \"type\": \"text\",\n" +
            "      \"analyzer\": \"ik_max_word\",\n" +
            "      \"search_analyzer\": \"ik_smart\"\n" +
            "    },\n" +
            "    \"content\": {\n" +
            "      \"type\": \"text\",\n" +
            "      \"analyzer\": \"ik_max_word\",\n" +
            "      \"search_analyzer\": \"ik_smart\"\n" +
            "    },\n" +
            "    \"userId\": {\n" +
            "      \"type\": \"long\"\n" +
            "    },\n" +
            "    \"createTime\": {\n" +
            "      \"type\": \"date\"\n" +
            "    }\n" +
            "  }\n" +
            "}", XContentType.JSON
        );
        
        client.indices().create(request, RequestOptions.DEFAULT);
    }
}

2. 全文检索实现

2.1 搜索服务
@Service
public class SearchService {
    
    @Autowired
    private RestHighLevelClient client;
    
    public PageResult<PostVO> searchPosts(String keyword, Integer pageNum, Integer pageSize) {
        // 1. 构建搜索请求
        SearchRequest request = new SearchRequest("posts");
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(
            QueryBuilders.multiMatchQuery(keyword, "title", "content")
                .field("title", 3.0f)  // 标题权重更高
        );
        sourceBuilder.from((pageNum - 1) * pageSize);
        sourceBuilder.size(pageSize);
        sourceBuilder.highlighter(
            new HighlightBuilder()
                .field("title")
                .field("content")
                .preTags("<em>")
                .postTags("</em>")
        );
        request.source(sourceBuilder);
        
        // 2. 执行搜索
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        
        // 3. 处理结果
        List<PostVO> posts = new ArrayList<>();
        for (SearchHit hit : response.getHits().getHits()) {
            PostVO post = JSON.parseObject(hit.getSourceAsString(), PostVO.class);
            // 设置高亮内容
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            if (highlightFields.containsKey("title")) {
                post.setTitle(highlightFields.get("title").getFragments()[0].string());
            }
            if (highlightFields.containsKey("content")) {
                post.setContent(highlightFields.get("content").getFragments()[0].string());
            }
            posts.add(post);
        }
        
        return new PageResult<>(posts, response.getHits().getTotalHits().value);
    }
}

3. 热门话题

3.1 话题热度计算
@Service
public class TopicService {
    
    private static final String TOPIC_SCORE_KEY = "topic:score";
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    public void incrementTopicScore(String topic) {
        // 1. 更新话题分数
        redisTemplate.opsForZSet().incrementScore(TOPIC_SCORE_KEY, topic, 1);
    }
    
    public List<TopicVO> getHotTopics(int limit) {
        // 1. 获取热门话题
        Set<ZSetOperations.TypedTuple<Object>> typedTuples = 
            redisTemplate.opsForZSet().reverseRangeWithScores(TOPIC_SCORE_KEY, 0, limit - 1);
            
        // 2. 转换为VO对象
        return typedTuples.stream()
            .map(tuple -> {
                TopicVO vo = new TopicVO();
                vo.setName((String) tuple.getValue());
                vo.setScore(tuple.getScore());
                return vo;
            })
            .collect(Collectors.toList());
    }
}

4. 智能推荐

4.1 基于用户行为的推荐
@Service
public class RecommendService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    private static final String USER_VIEW_KEY = "user:view:";
    private static final String USER_LIKE_KEY = "user:like:";
    
    public List<PostVO> recommendPosts(Long userId) {
        // 1. 获取用户行为数据
        Set<Object> viewPosts = redisTemplate.opsForSet().members(USER_VIEW_KEY + userId);
        Set<Object> likePosts = redisTemplate.opsForSet().members(USER_LIKE_KEY + userId);
        
        // 2. 获取相似用户
        List<Long> similarUsers = findSimilarUsers(userId, viewPosts, likePosts);
        
        // 3. 获取推荐帖子
        return getRecommendPosts(userId, similarUsers);
    }
    
    private List<Long> findSimilarUsers(Long userId, Set<Object> viewPosts, Set<Object> likePosts) {
        // 基于协同过滤算法找到相似用户
        // 1. 计算用户行为向量
        // 2. 计算用户相似度
        // 3. 返回相似度最高的用户列表
        return similarUsers;
    }
    
    private List<PostVO> getRecommendPosts(Long userId, List<Long> similarUsers) {
        // 1. 获取相似用户的帖子
        List<Post> candidatePosts = postMapper.selectList(
            new LambdaQueryWrapper<Post>()
                .in(Post::getUserId, similarUsers)
                .orderByDesc(Post::getCreateTime)
        );
        
        // 2. 过滤和排序
        return filterAndSortPosts(userId, candidatePosts);
    }
}
4.2 推荐结果缓存
@Service
public class RecommendService {
    
    private static final String RECOMMEND_CACHE_KEY = "recommend:";
    private static final long RECOMMEND_CACHE_TTL = 3600L; // 1小时
    
    public List<PostVO> getCachedRecommendPosts(Long userId) {
        String key = RECOMMEND_CACHE_KEY + userId;
        
        // 1. 尝试从缓存获取
        List<PostVO> cachedPosts = (List<PostVO>) redisTemplate.opsForValue().get(key);
        if (cachedPosts != null) {
            return cachedPosts;
        }
        
        // 2. 计算推荐结果
        List<PostVO> recommendPosts = recommendPosts(userId);
        
        // 3. 存入缓存
        redisTemplate.opsForValue().set(key, recommendPosts, RECOMMEND_CACHE_TTL, TimeUnit.SECONDS);
        
        return recommendPosts;
    }
}

5. 性能优化

  1. 搜索优化

    • 使用ES的分词器优化
    • 结果缓存
    • 异步索引更新
  2. 实时通讯优化

    • 心跳检测
    • 消息压缩
    • 连接池管理
  3. 推荐系统优化

    • 离线计算
    • 定时更新
    • 多级缓存

6. 安全考虑

  1. 私信安全

    • 消息加密
    • 频率限制
    • 敏感词过滤
  2. WebSocket安全

    • 身份认证
    • 连接限制
    • 消息验证
  3. 数据安全

    • 索引权限控制
    • 数据脱敏
    • 访问控制

这些功能的实现需要考虑:

  1. 可扩展性

    • 模块化设计
    • 接口抽象
    • 配置化管理
  2. 可维护性

    • 代码规范
    • 完善注释
    • 异常处理
  3. 监控告警

    • 性能监控
    • 错误日志
    • 业务指标
  4. 测试验证

    • 单元测试
    • 压力测试
    • 集成测试

http://www.kler.cn/a/440962.html

相关文章:

  • java求职学习day23
  • 愿景:做机器视觉行业的颠覆者
  • 《LLM大语言模型+RAG实战+Langchain+ChatGLM-4+Transformer》
  • 房屋租赁系统在数字化时代中如何重塑租赁服务与提升市场竞争力
  • 哈希表实现
  • MySQL(高级特性篇) 14 章——MySQL事务日志
  • Vue3实现双向绑定的基本原理和代码示例解析
  • HAL库与标准库的GPIO配置结构体对比
  • 免费下载 | 2024全球AI网络安全产品洞察报告
  • Python-基于Pygame的小游戏(贪吃蛇)(一)
  • Flink State面试题和参考答案-(下)
  • 数智读书笔记系列003 深度学习革命 从历史到未来
  • C++ ofstream:写操作
  • 如何在服务器上安装 Maven
  • busybox学习——简单介绍
  • 学习记录(13):VR晕动症-VR Motion Sickness
  • springcloud eureka原理和机制
  • 吉利百度发表联合声明:将积极协助极越处理相关事宜
  • HIK 相机 设置缓存节点进行取流
  • 模板方法模式详解:定义程序骨架与框架设计
  • MongoDB-副本集
  • Java函数式编程【三】【Stream终止操作】【上】之【简单约简】
  • 跑步训练(蓝桥杯2020试题A)
  • 微知-python包管理工具pip如何查看安装了某个库?(pip3 show xxx;pip3 list; pip3 show xxx -v)
  • 自动驾驶---小米汽车智驾进展
  • React状态管理常见面试题目(一)