热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

仿牛客网论坛项目

本文主要分享【】,技术文章【仿牛客网论坛项目】为【weixin_42157700】投稿,如果你遇到相关问题,本文相关知识或能到你。1.注册模块用户进入首页之后,点击注册按钮,就跳转到了

本文主要分享【】,技术文章【仿牛客网论坛项目】为【weixin_42157700】投稿,如果你遇到相关问题,本文相关知识或能到你。

1. 注册模块

用户进入首页之后,点击注册按钮,就跳转到了注册页面。注册主要包括,用户名,密码,以及邮箱。

在注册的时候会对用户的注册信息进行校验。首先通过commons.lang3包下的StringUtils对参数判空。然后还需要判断用户名以及邮箱是否是重复的,这个需要取表中根据用户或者是邮箱判断有没有对应的用户,如果有这个邮箱或者是用户名已经注册过,那么就需要重新输入。如果校验都通过了,那么接下来就是将用户的信息写入到数据库中的user表中。而对于用户的密码,为了保证安全性,会将用户的密码加上salt之后,使用md5进行加密,加密之后的密码才会写入到数据库中。注意salt是使用UUID随机生成的字符串。还会生成一个随机的激活码用作后面的激活用,以及为用户分配一个默认的头像,这个头像存的是对应的url地址。然后将信息插入到表中。此时新注册的用户的status0,表示没有被激活过。注册完毕之后,跳转到首页。给用户对应的邮箱发送一封邮件,邮件的内容是一个包含用户id和激活码的url,用户点击url,就可以完成用户的激活功能,就会将用户的status从0设置为1.激活成活会跳转到登录页面,否则会跳转到首页。

 

表中的字段:id是一个自增的主键。

username:用户注册的用户名。password:是加密之后的密码。salt:用户密码加密所使用的salt。email:用户注册使用的邮箱。type:用户类型,0-普通用户; 1-超级管理员; 2-版主; status:状态,0-未激活; 1-已激活;激活需要使用邮箱进行验证。actuvation_code:激活码。header_url:设置用户的头像的url。create_time:用户注册时间。

2. 登录模块

当用户点击登录按钮的时候,会跳转到登录页面。

登录的时候,会验证用户的账号是否存在,密码是否正确,验证码是否正确。验证码是通过kaptcha类来实现的,验证码的范围是是a-z以及1-9组成的4位随机字符,会存放在redis中,给验证码设置了一分钟的有效时间,并且将验证码会以图片的形式显示在浏览器中。

在成功登录之后,就会生成一个登录凭证,这个登录凭证是通过UUID生成的随机字符串。将这个登录凭证记录保存在redis中,并且会将这个登录凭证设置到COOKIE中,当用户发起某些请求的时候,就会在COOKIE中带上这个登录凭证。服务器就可以直接去redis中查询有没有对应的登录凭证,如果有的话,说明用户就已经登录过了,这样子避免了每次都要去数据库中进行查询,减少了与磁盘的IO提升了性能。同时用户的登录凭证会设置过期时间。在这里默认是1个小时。

用户成功登录之后,会显示用户的登录信息,这个可以靠拦截器来实现。

3. 核心模块

核心模块包括用户发布帖子,评论帖子,显示帖子的详情以及私信列表

4. 发布帖子

发布帖子是用到了AJAX,它可以异步发送请求,使网页能够将增量更新呈现在页面上,而不需要刷新整个页面,这里使用了jQuery来发送AJAX请求,以实现发布帖子的功能。

用户成功登录之后,用户就可以发布一个帖子。在发布帖子的时候,为了保证用户是登录状态,使用了一个ThreadLocal类保存每一个用户的信息,因为每个用户都可以看作是一个线程,使用ThreadLocal也可以保证线程的安全性。通过ThreadLocal类的对象,可以查询当前用户是否为空,如果为空的话,那么就需要重新登录,不为空的话,就说明用户已经登录,就可以发布帖子。并且对用户发布的帖子的内容通过前缀树实现了敏感词过滤。使用前缀树存储敏感词,对text中的敏感词实现替换。帖子发布成功的同时,也会插入到对应的discuss_port表中。

帖子表

 

discuss_post:帖子的表名

Id:表示的是帖子的id,是一个递增的主键。User_id发送帖子的用户。title帖子的标题,content帖子的内容。type:帖子的类型,0-普通; 1-置顶;status:帖子的状态,0-正常; 1-精华; 2-黑;。Create_time:帖子创建的时间。Commnent_count:帖子评论的数量。Score:分数。

5. 帖子评论

帖子里面会有评论,首先需要创建评论的表,注意的是评论的时候,可以对帖子进行评论也可以对评论进行回复。所以在评论的时候,需要根据评论的对象指定评论的类型。

评论的id,自增主键,user_id,发送评论

的用户。Entity_type:实体类型,0表示帖子,1表示评论。Entity_id:评论的具体目标的id,如果评论的帖子那么就是帖子的id,如果评论的是别人的评论的话,那么就是评论的id,target_id,具体是对哪个用户进行评论,这是具体用户的id,content:回复的内容。status,状态,0表示有效的评论,1表示的无效的评论。create_time:创建时间,

 

在通过帖子中用户的id,查询具体用户的时候,会涉及到关联查询,这里采用的是分步查询,先查帖子的详细信息,然后通过用户的id查询到用户的具体信息。

添加评论,首先用户也必须要使用到ThreadLocal类用来检测当前用户的登录状态,只有用户是登录状态才能够评论。评论分为两种,一种是对用户的帖子进行评论,第二种是对用户的评论进行回复。在添加评论的同时,也需要增加帖子中的评论数的数量。为了保证操作的原子性,这两个操作需要放入到一个事务中。事务的实现是使用@Transactional注解,隔离级别设置为了读已提交,事务的传播行为为Required

@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)

6. 显示帖子详情

显示帖子的详情,就需要显示帖子的标题,内容,作者信息,日期,以及帖子的评论。

首先是帖子的标题,内容创建日期这些直接在帖子discuss_post表中就可以查询,而查询用户的信息,就需要进行关联查询,这里采用分布查询,先得到帖子的详细信息,根据帖子详细信息中用户的id查询用户的详细信息。

在显示帖子评论的时候,需要去帖子表discuss_post表中查询,这里利用了分页查询会显示当前页的帖子的数据,以及显示当前帖子的评论数包括对评论的回复数。分页查询的实现就是简单的通过mysql中的limit来实现的。处理评论之外,还有对评论的回复,对于评论的回复也是需要显示的,这里可以通过查到的评论,根据评论的id去查询对应的回复即可。

因为回复或者是评论也是帖子详情的一部分,所以回复和评论的处理也是放在帖子的contrtoller中,也就是PostController中。

在controller中,显示查询到帖子的信息以及还需要用户的信息。可以使用mtybtais的关联查询,但是这种可能会造成一些信息的冗余,就是有时候我们并不需要这些信息,也会将这些信息查询到,所以这里采用的分步查询,先通过id查询到帖子,然后根据帖子查询到用户。

除此之外,还有每条帖子所对应的评论信息。

首先根据Entity_type值为0以及帖子的id获取这个帖子中所有评论的集合。

然后遍历这个list,获取每条评论的具体信息。每条评论的具体信息放入到一个map中。

在每条评论中都会包含若干条的回复信息。通过一个list来获取每条评论中的回复的集合。同样用一个map来记录每条回复的具体信息。

除此之外,还会再每条评论上展示每个评论下的回复数量,因此还需要展示每个回复的数量。

7. 私信列表

两个用户之间可以相互发送私信。除了发送私信之外,还支持查询某个用户的会话列表,在每个会话中只显示一条最新的私信。查询每个会话中未读消息的个数,以及当前用户的未读的消息的总个数。

私信列表对应的表是message表,

message表:id是一个自增的主键,from_id表示的当前用户,to_id表示与之会话的用户,conversation_id,这次会话的id,有两个用户的id拼接而成,按照用户的id从小到大进行排序。这样子虽然会有冗余信息,但是查询起来会更加的方便。content表示的会话的内容,status表示的是当前会话的状态,0-未读;1-已读;2-删除;create_time:创建的时间。

1)发送私信

发送私信需要指定私信的用户的用户名以及私信的内容,通过用户名查询到与之私信的用户的id,消息成功发送之后,就插入到message表中。

2)显示当前用户的会话列表

因为与当前用户私信的用户可能有多个,所以显示会话列表的时候,还是会进行分页显示。并且每个会话,会显示当前会话的最后一条消息以及当前会话未读消息的数量。

3)查看某个会话的详情

因为当前会话也会有多条,所以显示的时候还是会进行分页显示。并且需要将未读的消息都改为已读,这样子当前会话就不会再显示未读消息的数量。

根据当前的会话id查询,当前会话的所有信息,主要是私信的消息。

性能模块主要包含两个部分,一个是用户点赞功能,一个是缓存用户的信息,都是基于redis实现的。

8.优化模块

(1)点赞功能

点赞功能支持对帖子点赞,对评论点赞。

在首页显示每个帖子的点赞数,在帖子的详情页显示每个评论的点赞数,以及每条回复的点赞数。

同时,后面还需要开发一个用户的个人页面,因此还需要统计一个用户的所有的点赞数。

在这里使用redis来实现点赞的功能。

对于每个实体,也就是帖子或者是评论或者是回复,使用set来存储数据。

为什么对于每个实体使用set类型。set中存的是用户的user_id.

首先每个用户只能对一个帖子,一个评论以及一个回复进行点赞。如果是第二次点赞的话,那么就是取消赞。所以每次在点赞的时候,我们都需要先判断一下当前用户在不在set集合中,如果在的话,那么就是取消点赞,也就是将当前的用户移除set。并且响应的赞数-1,如果不在的话,那么就是点赞。将当前用户加入到set中,并且响应的赞数+1.

第二,通过统计set的长度就可以得到每个实体(每个帖子,每个评论,每个回复的点赞数)

因为在给帖子或者是其他实体点赞的时候,也需要给对应的用户的点赞数+1,因此需要将帖子点赞,以及用户的点赞这两个操作放入到一个事务中执行。

(2)用redis缓存用户数据

频繁的去数据库中取用户的信息,也是很浪费时间的,所以可以将某些用户的信息放入到redis中,并且需要设置过期时间。当用户的信息被修改之后,而用户的信息还没有过期的话,那么就先更新数据库的信息,然后将redis中的信息删除掉。等下次再次访问用户的时候,就将用户的信息从数据库中写入到redis中。将用户的信息的过期时间设置为3600s。

9.系统通知模块

1)概述

当有用户评论了我的帖子(评论)或者是对我的帖子(评论)进行点赞,那么系统就会自动发送一条通知给对应的用户。这个功能主要是借助消息队列来实现的,这里采用的消息队列是kafka。

当前用户对某个帖子评论或者是某个评论回复,就将会发送一条系统通知给对应的用户。

如果是对帖子评论的话,可以通过查询帖子的id,取帖子表中找到帖子的作者,这个作者就是消费者。

如果是对评论回复的话,可以通过查询评论表,找到评论表中的target_id,这个就是tagert_id对应的用户就是消费者。

如果是对评论点赞的话,那么由两种情况,一种是如果是取消点赞,那么就不需要发送系统通知,如果是点赞的话,那么就发送一个系统通知。首先需要判断当前用户是否已经点赞过,然后如果是没有点赞过,那么就将触发点赞事件。生产者就需要生产一条对应的消息。

系统给用户发送消息,其实也相当于系统与用户之间的一次会话,当消费成功接收到消息之后,对应的会话的消息也会保存在message表中。message表中from_id将会被设置为1,表示这是系统用户。同样也可以查询系统与用户之间的私信列表。与前面的两个用户之间的私信列表类似,也会采用分页的方式显示,因为这里分了两个主题,相当于有两个会话列表,每个会话中显示也会话的最后一条消息。也会显示会话中未读消息的数量等信息。当用户查询某个会话的时候,对应的会话中的未读消息都会变为已读。

2)为什么采用消息队列

采用消息队列,针对评论、点赞这两种不同的事,可以定义两类不同的主题(评论、点赞),发生相应的事件可以将其包装成一条消息放入对应的队列里。那么当前的线程可以继续处理下一条消息,不用等待系统确认,也就是实现异步通信。在本次实验中,由于数据量比较少,只采用了一台broker,只有一个分区和一个副本。

3)消息队列的具体实现

1)创建一个event实体类,对事件进程封装。

包括发送的主题,userId,entityType表示实体的类型,(评论或者是点赞),entity实体的id,

entityUserId将消息发送给哪个用户。用一个map来封装其他类型的数据。

2)生产者向指定的主题,发送事件。

3)消费者

消费者,首先要在方法上加上@KafkaListener(topics = {TOPIC_COMMENT, TOPIC_LIKE})这个注解,声明消费者需要消费的主题。

因为消息是被序列化为Json字符串的,所以消费者首先需要对Json的数据进行解析。

需要将得到的消息封装为Message对象,存入到数据库中。

前面说过,message这张表有一个userId表示的是发送消息的id,因此在这里,消息是由系统发出的,所以这个用户ID可以设置为系统id。然后message表中的目标id就是这里的接收消息的用户的id。在messsage中会有一个接收会话id,前面说的是当前对话的这个用户的拼接而成,在这里设置为这次消息的topic。时间就是当前时间。

创建一个map,用来存储生产者的id,实体的类型,实体的id。以及最后某些额外的数据。

然后将map中的数据作为message表中的contenet存入。存入之前,需要将map转换为json格式的字符串。

10. 其他模块

1)网站数据的统计

主要是计算网站每日的访问量以及统计网站活跃的用户。

UV:按IP地址统计每日的访问量;DAU:按用户IP统计活跃用户。

计算key

 

2)热帖排行

帖子的得分=log(帖子评论数*10+帖子点赞数*2)+(帖子发布时间-项目发布的时间)

给每个帖子定义一个score参数,计算方式如上。更新帖子分数有两种方法:第一种是一旦触发相关帖子的操作就立刻计算帖子分数更新,但是这样效率会非常低,有可能触发某一事件,帖子分数还在计算中又有另一个事件,而且时间也会影响帖子分数,没过一秒计算一次不合时宜。因此第二种方式就是采用定时任务的方式,每隔一段时间进行帖子分数更新(这里采用五分钟算一次)。但是我们没必要时间一到将所有的帖子都更新一遍,尤其是当数据量很大的时候,因此这里是采用的一种方法,将这段时间内有评论点赞等等操作的帖子ID,先加载到缓存中(这里是存到redis中),等定时到了,将缓存中的修改过的帖子的分数进行更新,这样数据量比较小,效率也比较高。在这里为了实现这个功能,引入了分布式定时任务Spring Quartz来解决定时问题。

Spring Quartz简介:

Scheduler是Quartz中的核心接口,该类实例内部维护了一个JobDetails和Trigger的列表,主要执行调度工作,如添加、删除、获取作业列表、自动或手动触发作业等操作。通过制定一个job任务,接着JobDetails是具体的实现操作,Trigger是触发作业的频率和时间。它是将信息存入表中,后面的操作都是从表中进行取数据。

实现步骤如下:

1)常规操作,在触发发帖事件时将帖子ID存入Redis中对应的Key中,value用一个set存储用户ID,还可以起到去重的作用。同样修改评论、点赞的Controller,触发操作时将用户ID加入到对应的redis的Key中。

2)创建帖子记分定时任务PostScoreRefreshJob,创建刷新帖子分数的方法。

3)在QuartzConfig中添加定时任务刷新分数的postScoreRefreshJobDetail和postScoreRefreshTrigger

3)热门帖子的缓存

优化热门帖子的列表。将热门帖子加入到缓存中,这里设置了两级缓存,第一缓存是本地缓存

caffeine,第二个是redis缓存。在热门帖子浏览的方法中,首先会去本地缓存中查询帖子的信息,如果没有命中的话,就去redis中查询,如果还没有命中,就通过service调用dao进行查询。然后将数据填充到redis中以及本地缓存中。

本文《仿牛客网论坛项目》版权归weixin_42157700所有,引用仿牛客网论坛项目需遵循CC 4.0 BY-SA版权协议。


推荐阅读
  • 一.常见基于身份识别进行反爬1通过headers字段来反爬headers中有很多字段,这些字段都有可能会被对方服务器拿过来进行判断是否为爬虫1.1通过headers中的User-A ... [详细]
  • 如何提高PHP编程技能及推荐高级教程
    本文介绍了如何提高PHP编程技能的方法,推荐了一些高级教程。学习任何一种编程语言都需要长期的坚持和不懈的努力,本文提醒读者要有足够的耐心和时间投入。通过实践操作学习,可以更好地理解和掌握PHP语言的特异性,特别是单引号和双引号的用法。同时,本文也指出了只走马观花看整体而不深入学习的学习方式无法真正掌握这门语言,建议读者要从整体来考虑局部,培养大局观。最后,本文提醒读者完成一个像模像样的网站需要付出更多的努力和实践。 ... [详细]
  • adfs是什么_培训与开发的概念
    adfs是什么_培训与开发的概念(如您转载本文,必须标明本文作者及出处。如有任何疑问请与我联系me@nap7.com)ADFS相关开发技术的中文资料相对匮乏,之前在弄这个东西的时候 ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • 关于我们EMQ是一家全球领先的开源物联网基础设施软件供应商,服务新产业周期的IoT&5G、边缘计算与云计算市场,交付全球领先的开源物联网消息服务器和流处理数据 ... [详细]
  • 本文介绍了通过ABAP开发往外网发邮件的需求,并提供了配置和代码整理的资料。其中包括了配置SAP邮件服务器的步骤和ABAP写发送邮件代码的过程。通过RZ10配置参数和icm/server_port_1的设定,可以实现向Sap User和外部邮件发送邮件的功能。希望对需要的开发人员有帮助。摘要长度:184字。 ... [详细]
  • Linux如何安装Mongodb的详细步骤和注意事项
    本文介绍了Linux如何安装Mongodb的详细步骤和注意事项,同时介绍了Mongodb的特点和优势。Mongodb是一个开源的数据库,适用于各种规模的企业和各类应用程序。它具有灵活的数据模式和高性能的数据读写操作,能够提高企业的敏捷性和可扩展性。文章还提供了Mongodb的下载安装包地址。 ... [详细]
  • 如何在php文件中添加图片?
    本文详细解答了如何在php文件中添加图片的问题,包括插入图片的代码、使用PHPword在载入模板中插入图片的方法,以及使用gd库生成不同类型的图像文件的示例。同时还介绍了如何生成一个正方形文件的步骤。希望对大家有所帮助。 ... [详细]
  • .NetCoreWebApi生成Swagger接口文档的使用方法
    本文介绍了使用.NetCoreWebApi生成Swagger接口文档的方法,并详细说明了Swagger的定义和功能。通过使用Swagger,可以实现接口和服务的可视化,方便测试人员进行接口测试。同时,还提供了Github链接和具体的步骤,包括创建WebApi工程、引入swagger的包、配置XML文档文件和跨域处理。通过本文,读者可以了解到如何使用Swagger生成接口文档,并加深对Swagger的理解。 ... [详细]
  • PatchODAX8: ... [详细]
  • 前言:关于跨域CORS1.没有跨域时,ajax默认是带cookie的2.跨域时,两种解决方案:1)服务器端在filter中配置详情:http:blog.csdn.netwzl002 ... [详细]
  • PHP输出缓冲控制Output Control系列函数详解【PHP】
    后端开发|php教程PHP,输出缓冲,Output,Control后端开发-php教程概述全景网页源码,vscode如何打开c,ubuntu强制解锁,sts启动tomcat慢,sq ... [详细]
author-avatar
我是小崔来啦
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有