小工具知识网

当前位置: 首页 > 行业知识 > 仿牛客网项目---社区首页的开发实现

行业知识

仿牛客网项目---社区首页的开发实现

2026-10-29 16:01:00 来源:互联网转载

从今天开始���们来写一个新项目,这个项目是一个完整的校园论坛的项目。主要功能模块:用户登录注册,帖子发布和热帖排行,点赞关注,发送私信,消息通知,社区搜索等。这篇文章我们先试着写一下用户的登录注册功能。

我们做web项目,一般web项目是主要解决浏览器和服务器之间交互的问题。而浏览器和服务器是由一次一次的请求交互的。因此,任何功能都可拆解成若干次请求,其实只要掌握好每次请求的执行过程,按照步骤开发每一次请求,基本上项目就可以逐步完善起来。

一次请求的执行过程:

其实最好是可以把功能做拆解,第一步先实现什么效果,第二步再完善这个效果。因为你不可能一下子就一步到位写出完整的代码的,是吧?

所以这就是我的思路,我是想着一个功能一个功能的搞定。这篇文章来搞定开发社区首页。

首页有什么?不就是帖子,还有用户头像,还有分页嘛,因此接下来我们就实现这三个功能。

社区首页模块大致结构

entity文件夹:Page.java

DAO层:DiscussionMapper

Service层:DiscussionPostMapper

Controller层:HomeController

Dao层---DiscussPostMapper

@Mapperpublic interface DiscussPostMapper {    List selectDiscussPosts(int userId, int offset, int limit, int orderMode);    // @Param注解用于给参数取别名,    // 如果只有一个参数,并且在里使用,则必须加别名.    int selectDiscussPostRows(@Param("userId") int userId);    int insertDiscussPost(DiscussPost discussPost);    DiscussPost selectDiscussPostById(int id);    int updateCommentCount(int id, int commentCount);    int updateType(int id, int type);    int updateStatus(int id, int status);    int updateScore(int id, double score);}

这是一个名为DiscussPostMapper的接口,用于定义帖子相关的数据库操作方法 :

  1. selectDiscussPosts(int userId, int offset, int limit, int orderMode):根据用户ID、偏移量、限制数量和排序方式从数据库中查询帖子列表。

  2. selectDiscussPostRows(int userId):查询帖子总数。根据用户ID统计数据库中的帖子数量。

  3. insertDiscussPost(DiscussPost discussPost):将帖子信息插入数据库。

  4. selectDiscussPostById(int id):根据帖子ID从数据库中查询帖子信息。

  5. updateCommentCount(int id, int commentCount):更新帖子的评论数量。根据帖子ID更新帖子的评论数量信息。

  6. updateType(int id, int type):更新帖子的类型。根据帖子ID更新帖子的类型信息。

  7. updateStatus(int id, int status):更新帖子的状态。根据帖子ID更新帖子的状态信息。

  8. updateScore(int id, double score):更新帖子的分数。根据帖子ID更新帖子的分数信息。

Service层---DiscussPostService

@Servicepublic class DiscussPostService {    @PostConstruct    public void init() {        // 初始化帖子列表缓存        postListCache = Caffeine.newBuilder()                .maximumSize(maxSize)                .expireAfterWrite(expireSeconds, TimeUnit.SECONDS)                .build(new CacheLoader() {                    @Nullable                    @Override                    public List load(@NonNull String key) throws Exception {                        if (key == null || key.length() == 0) {                            throw new IllegalArgumentException("参数错误!");                        }                        String[] params = key.split(":");                        if (params == null || params.length != 2) {                            throw new IllegalArgumentException("参数错误!");                        }                        int offset = Integer.valueOf(params[0]);                        int limit = Integer.valueOf(params[1]);                        // 二级缓存: Redis -> mysql                        logger.debug("load post list from DB.");                        return discussPostMapper.selectDiscussPosts(0, offset, limit, 1);                    }                });        // 初始化帖子总数缓存        postRowsCache = Caffeine.newBuilder()                .maximumSize(maxSize)                .expireAfterWrite(expireSeconds, TimeUnit.SECONDS)                .build(new CacheLoader() {                    @Nullable                    @Override                    public Integer load(@NonNull Integer key) throws Exception {                        logger.debug("load post rows from DB.");                        return discussPostMapper.selectDiscussPostRows(key);                    }                });    }    public List findDiscussPosts(int userId, int offset, int limit, int orderMode) {        if (userId == 0 && orderMode == 1) {            return postListCache.get(offset + ":" + limit);        }        logger.debug("load post list from DB.");        return discussPostMapper.selectDiscussPosts(userId, offset, limit, orderMode);    }    public int findDiscussPostRows(int userId) {        if (userId == 0) {            return postRowsCache.get(userId);        }        logger.debug("load post rows from DB.");        return discussPostMapper.selectDiscussPostRows(userId);    }    public int addDiscussPost(DiscussPost post) {        if (post == null) {            throw new IllegalArgumentException("参数不能为空!");        }        // 转义HTML标记        post.setTitle(HtmlUtils.htmlEscape(post.getTitle()));        post.setContent(HtmlUtils.htmlEscape(post.getContent()));        // 过滤敏感词        post.setTitle(sensitiveFilter.filter(post.getTitle()));        post.setContent(sensitiveFilter.filter(post.getContent()));        return discussPostMapper.insertDiscussPost(post);    }    public DiscussPost findDiscussPostById(int id) {        return discussPostMapper.selectDiscussPostById(id);    }    public int updateCommentCount(int id, int commentCount) {        return discussPostMapper.updateCommentCount(id, commentCount);    }    public int updateType(int id, int type) {        return discussPostMapper.updateType(id, type);    }    public int updateStatus(int id, int status) {        return discussPostMapper.updateStatus(id, status);    }    public int updateScore(int id, double score) {        return discussPostMapper.updateScore(id, score);    }}

这是一个名为DiscussPostService的服务类,用于处理帖子相关的业务逻辑。这段代码是真的烦,一大堆东西,不过我总结了一下,大概这段代码的功能差不多是这样的:

  1. 通过DiscussPostMapper进行数据库操作,包括查询帖子列表、帖子总数,插入帖子,根据帖子ID查询帖子信息,以及更新帖子的评论数量、类型、状态和分数。

  2. 使用SensitiveFilter进行敏感词过滤,对帖子的标题和内容进行转义和过滤。

  3. 使用Caffeine缓存库对帖子列表和帖子总数进行缓存,提高访问性能。缓存设置了最大容量和过期时间。

  4. 在初始化方法init()中,配置了帖子列表缓存和帖子总数缓存的加载方式,当缓存中不存在数据时,会从数据库中加载数据。

  5. 提供了方法如findDiscussPosts()和findDiscussPostRows()来获取帖子列表和帖子总数,如果缓存中有数据,则直接从缓存中获取,否则从数据库中获取。

  6. 提供了方法如addDiscussPost()来添加帖子,对帖子的标题和内容进行处理后插入数据库。

  7. 该服务类使用了注解@Service,表示该类是一个Spring的服务组件。

Controller层---HomeController

@Controllerpublic class HomeController implements CommunityConstant {    @RequestMapping(path = "/", method = RequestMethod.GET)    public String root() {        return "forward:/index";    }    @RequestMapping(path = "/index", method = RequestMethod.GET)    public String getIndexPage(Model model, Page page,                               @RequestParam(name = "orderMode", defaultValue = "0") int orderMode) {        // 方法调用前,SpringMVC会自动实例化Model和Page,并将Page注入Model.        // 所以,在thymeleaf中可以直接访问Page对象中的数据.        page.setRows(discussPostService.findDiscussPostRows(0));        page.setPath("/index?orderMode=" + orderMode);        List list = discussPostService                .findDiscussPosts(0, page.getOffset(), page.getLimit(), orderMode);        List discussPosts = new ArrayList();        if (list != null) {            for (DiscussPost post : list) {                Map map = new HashMap();                map.put("post", post);                User user = userService.findUserById(post.getUserId());                map.put("user", user);                long likeCount = likeService.findEntityLikeCount(ENTITY_TYPE_POST, post.getId());                map.put("likeCount", likeCount);                discussPosts.add(map);            }        }        model.addAttribute("discussPosts", discussPosts);        model.addAttribute("orderMode", orderMode);        return "/index";    }    @RequestMapping(path = "/error", method = RequestMethod.GET)    public String getErrorPage() {        return "/error/500";    }    @RequestMapping(path = "/denied", method = RequestMethod.GET)    public String getDeniedPage() {        return "/error/404";    }}

这是一个名为HomeController的控制器类,用于处理主页相关的请求。

  1. 通过DiscussPostService获取帖子相关的信息,包括帖子列表和帖子总数。

  2. 通过UserService获取用户相关的信息,包括发帖用户的信息。

  3. 通过LikeService获取帖子的点赞数量。

  4. 提供了root()方法,将根路径的请求转发到"/index"路径。

  5. 提供了getIndexPage()方法,处理主页的GET请求,根据页面参数orderMode获取帖子列表,并将数据添加到Model中供前端页面渲染。

entity文件夹---Page

/** * 封装分页相关的信息. */public class Page {    // 当前页码    private int current = 1;    // 显示上限    private int limit = 10;    // 数据总数(用于计算总页数)    private int rows;    // 查询路径(用于复用分页链接)    private String path;    public int getCurrent() {        return current;    }    public void setCurrent(int current) {        if (current >= 1) {            this.current = current;        }    }    public int getLimit() {        return limit;    }    public void setLimit(int limit) {        if (limit >= 1 && limit = 0) {            this.rows = rows;        }    }    public String getPath() {        return path;    }    public void setPath(String path) {        this.path = path;    }    /**     * 获取当前页的起始行     *     * @return     */    public int getOffset() {        // current * limit - limit        return (current - 1) * limit;    }    /**     * 获取总页数     *     * @return     */    public int getTotal() {        // rows / limit [+1]        if (rows % limit == 0) {            return rows / limit;        } else {            return rows / limit + 1;        }    }    /**     * 获取起始页码     *     * @return     */    public int getFrom() {        int from = current - 2;        return from  total ? total : to;    }}

这个实体类用于在分页查询中保存分页相关的信息,例如当前页码、每页显示的数量、数据总数等,以便在前端页面进行分页展示和生成分页链接。

至此,社区首页的一些简单的功能就实现了。其实这并不复杂,就是代码有点多而已。

上一篇:柯南存档怎么改服务器

下一篇:CSS搜索功能实战教程,轻松打造网页搜索利器!