牛客网项目总结
数据库
用户表,包括 id、用户名、密码、盐值、邮箱、类型(普通/管理员/版主)、状态(激活/未激活)、激活码(随机字符串)、头像 url、注册时间。
登录凭证表,包括 id、用户 id、登陆凭证(随机字符串)、登录状态(有效/无效)、过期时间。
评论表,包括 id、评论用户 id(索引)、评论实体 id(索引)、评论类型(帖子/回复)、被评论目标 id、评论内容、评论状态(有效/无效)、评论时间。
帖子表,包括 id、发帖用户 id(索引)、标题、帖子内容、类型(普通/置顶)、评论数量、状态(普通/精华/拉黑)、发帖时间。
消息表,包括 id、发消息 id(索引)、收消息 id(索引)、会话 id(由发消息双方 id 拼接,索引)、内容、状态(未读/已读/删除)、发消息时间。
注册
判断注册合法性
- 利用 StringUtils 判断用户名、密码、邮箱是否非空。
- 分别通过用户名和邮箱查询是否已经注册(为数据库的用户名和邮箱字段添加索引)。
通过 set
方法为用户设置各项信息,包括 MD5 加密后的密码、激活码等,然后插入数据库。
给用户发送激活邮件
- 在新浪邮箱打开 SMTP 服务,引入
spring-boot-starter-mail
依赖。 - 在配置文件配置主机(smtp.sina.com)、端口(465)、邮箱、授权码、协议(smtps),设置 smtp.ssl.enable = true。
- 调用 JavaMailSender 的 API 发送邮件,激活 url 由用户 id 和用户的激活码拼接而成。点击激活 url 后由 controller 中的方法进行处理(成功/重复/失败),调用 Model 对象的
addAttribute
方法将结果返回前端。
登录
生成验证码
- 引入 kaptcha 依赖,将验证码的大小、范围、长度等属性封装到 Properties 对象,作为参数构造 Config 对象,再用 Config 对象作为 DeafultKaptcha 对象
setConfig
方法的参数为验证码设置属性。 - 在登录的 controller 处理验证码,设置页面的响应类型为 png,通过 ImageIO 的
write
方法将图片输出到浏览器。
判断验证码正确后,调用业务层处理
- 利用 StringUtils 判断用户名、密码是否非空,之后判断用户是否存在、用户是否激活、密码是否正确,将错误信息存到 map 集合。
- 如果全部合法,为用户生成一个包含过期时间的登录凭证,将凭证存入 redis 和 map 集合。
根据返回的 map 是否包含登陆凭证判断登陆状态
- 如果登录成功,将凭证存入 cookie 并重定向至首页。
- 如果登陆失败,将 map 中的错误信息添加到 Model 对象,返回登录页。
检查登录状态
只处理带有自定义注解的方法,防止用户在未登录情况下通过 url 访问没有权限的页面。
利用 ThreadLocal 创建 HostHolder 类,包括 set
、get
、remove
方法,模拟 session 存储用户信息。
通过实现 HandlerInterceptor 接口创建一个拦截器,在 preHandle
方法中通过查询是否有登录凭证的 cookie,如果有则通过登录凭证查询用户 ID,再通过用户 ID 查询用户。最后将用户放入 hostHolder 中,在本次请求中持有用户信息。
创建 @LoginRequired
自定义注解,作用范围在方法上,有效期为运行时。为需要在登录状态下调用的方法,例如修改密码、上传头像等方法上等加上自定义注解。
创建拦截器,在 preHandle
中判断方法是否添加了 @LoginRequired
注解,如果加了并且从 hostHolder 获取不到用户则拒绝访问。
发帖、评论、私信
敏感词过滤
- 创建静态内部类 TrieNode ,通过 boolean 结束符判断是否匹配到关键字尾部。
- 利用
@PostConstruct
注解,在构造方法执行后初始化字典树。 - 添加
filter
方法,利用双指针进行匹配,过滤敏感词。
发帖、评论、私信
- 对内容进行 HTML 转义以及过滤敏感词。
- 将信息插入数据库的帖子/评论/消息表。
点赞
创建 RedisKeyUtil 工具类,通过实体类型和实体 id 生成对应实体获得赞的 key。
点赞/取消点赞:
- 通过 RedisKeyUtil 获得实体点赞的 key,然后通过 RedisTemplate 的 API 操作,调用集合的
isMember
方法查询 userId 是否存在于对应集合中,如果存在则移除出点赞的用户集合,如果不存在则添加到点赞的用户集合。 - 通过 RedisTemplate 的
execute
方法实现事务,保证被点赞用户点和点赞用户的数据更新一致。通过isMember
方法查询用户的点赞状态,之后通过mutli
方法开启事务。
点赞数量:通过调用 set 集合的 size
方法查询元素个数。
点赞状态:通过 set 集合的 isMember
方法实现。
关注和粉丝
在 RedisUnitl 工具类增加两个方法
- 通过用户 id 和实体类型获得用户关注的实体集合的 key。
- 通过实体类型和实体 id 获得实体拥有的粉丝集合的 key。
当用户关注某实体时,
- 将实体 id 和时间作为 value 和 score 加入用户的关注集合。
- 将用户 id 和时间作为 value 和 score 加入实体的粉丝集合。
当用户取消关注某实体时,将实体从用户的关注集合移除,用户从实体的粉丝集合移除。
关注列表和粉丝列表
- 用户的关注列表,通过 zset 的
reverseRange
获取 value 即关注用户的 userId,再查询出 user,通过score
获取关注时间。 - 用户的粉丝列表,通过 zset 的
reverseRange
获取 value 即粉丝的 userId,再查询出 user,通过score
获取关注时间。 - 列表信息封装在 list 集合中,再将 list 添加到 Model 对象里。
Kafka
发送系统通知
创建 Event 类,封装事件对象,包括主题(评论、点赞、关注)、用户 id、实体类型、实体 id,以及一个 map 集合存放其它信息。
触发事件
通过 Event 获取事件类型,并将其封装成 JSON 数据,然后调用注入的 KafkaTemplate 实例的 send 方法发送。
消费事件
通过 @KafkaListener
注解,topic 包括了评论、点赞和关注。从 recored 中获取信息,封装成 Message 对象然后调用 addMessage
方法插入数据库。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。