# forum **Repository Path**: li-jinbao/forum ## Basic Information - **Project Name**: forum - **Description**: 一个模仿牛客的一个论坛项目 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2022-05-20 - **Last Updated**: 2022-08-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ### 此项目已作废请转到 [https://gitee.com/li-jinbao/multi-online-chat](https://gitee.com/li-jinbao/multi-online-chat) #### 数据库设计 ##### user | 列名 | 类型 | 说明 | | --------------- | ------------------------------------------------------------ | ------------------------------------ | | id | int auto_increment primary key | 用户id | | username | varchar(50) charset utf8mb4 null | 用户名 | | password | varchar(50) charset utf8mb4 null | 密码 | | salt | varchar(50) charset utf8mb4 null | 密码混淆段 | | email | varchar(100) charset utf8mb4 null | 邮箱 | | type | int null | 类型:0 普通用户 1 超级管理员 2 版主 | | status | int null | 0 未激活 1已激活 | | activation_code | varchar(100) charset utf8mb4 null | 激活码 | | header_url | varchar(200) charset utf8mb4 null | 头像链接 | | create_time | timestamp null on update CURRENT_TIMESTAMP | 注册时间 | ##### discuss_post | 列名 | 类型 | 说明 | | ------------- | ------------------------------------------------------------ | ------------------------ | | id | int auto_increment primary key | 文章id | | user_id | varchar(45) charset utf8mb4 null | 用户id | | title | varchar(100) charset utf8mb4 null | 文章标题 | | content | text charset utf8mb4 null | 文章正文 | | type | int null | 帖子类型: 0 普通 1 置顶 | | status | int null | 0 正常 1 精华 2 拉黑 | | create_time | timestamp null on update CURRENT_TIMESTAMP | 创建时间 | | comment_count | int null | 评论数量 | | score | double null | 分数 | ##### comment | 列名 | 类型 | 说明 | | ----------- | ---------------------------------- | ---------------------------- | | id | int auto_increment primary key | 评论id | | user_id | int null | 用户id | | entity_type | int null | 被评论的实体类型 | | entity_id | int null | 被评论的实体id | | target_id | int null | 评论中对于其他评论的回复目标 | | content | int null | 评论内容 | | status | int null | 状态 | | create_time | timestamp null | 创建时间 | ##### message | 列名 | 类型 | 说明 | | --------------- | ---------------------------------- | ---------------------- | | id | int auto_increment primary key | 消息id | | from_id | int null | 消息产生者(发送者)id | | to_id | int null | 消息消费者(接收者)id | | conversation_id | varchar(45) not null | 会话id | | content | text null | 消息内容 | | status | int null | 0-未读;1-已读;2-删除 | | create_time | timestamp null | 创建时间 | ##### login_ticket | 列名 | 类型 | 说明 | | ------- | ------------------------------------------------------------ | -------------------------- | | id | int auto_increment primary key | 登录凭证id | | user_id | int null | 用户id | | ticket | varchar(45) charset utf8mb4 null | 登录凭证 | | status | int null | 是否有效:0-有效;1-无效; | | expired | timestamp null on update CURRENT_TIMESTAMP | 过期时间 | 用户登陆时,在数据库创建登录凭证,并把token返回给客户端,客户端再次发起请求时在cookie中携带token,在数据中查找是否存在登录凭证,是否有效及是否过期。 #### 邮箱注册 流程图: ![注册流程图](https://pic.imgdb.cn/item/6288e3f409475431294d0468.png) 前台通过表单提交数据至`/register`,controller调用service层register服务,根据返回结果跳转相关页面。 service层校验用户提交的数据(如用户名是否存在,邮箱是否已经被注册等),然后对密码拼接上随机字符串之后再使用md5哈希函数算法生成哈希值存入数据库,拼接上随机字符串是防止黑客使用密码撞库操作破解密码。 然后根据用户提交的邮箱数据发送激活邮件(内含有有用户id和激活码的激活链接)。 用户收到并点击激活链接后,服务器检查用户id和激活码是否有效(如是否重复激活,是否用户id和激活码不合法),最后设置用户状态为已激活。 实际效果: ![](https://pic.imgdb.cn/item/6288e43a09475431294d3688.png) ![](https://pic.imgdb.cn/item/6288e43a09475431294d3696.png) ![](https://pic.imgdb.cn/item/6288e43a09475431294d367a.png) ![](https://pic.imgdb.cn/item/6288e43a09475431294d3680.png) #### 会话管理(登录,登出) 用户跳转到登录页面后自动获取验证码(或者手动重新获取验证码),服务器将验证码存入session,并添加Cookie。 用户提交登录请求后,服务端从session中获取验证码,并于请求中携带的验证码比较,成功则再验证账号密码是否正确,账户是否已激活,最后生成登录凭证ticket存入数据库,在cookie中返回登录凭证。 登出时服务端根据前端的登陆凭证,更新数据库中的登陆凭证为无效状态。 ![](https://pic.imgdb.cn/item/62899a530947543129b6c963.png) 实际效果: ![](https://pic.imgdb.cn/item/62899ab10947543129b710f6.jpg) #### 缓存优化 使用Redis缓存验证码,登录凭证,用户信息,既减小了服务器压力,又能解决未来分布式信息共享问题。 ##### **代替session存储验证码** 理由如下: 验证码需要频繁的访问和刷新,对性能要求较高 验证码只需要暂存,不需要长期保存 如未来涉及到分布式部署,能避免session共享的问题。 ##### **存储登录凭证** 每次对网站的请求都会通过拦截器到数据库查询用户的登录凭证,因此考虑到凭证的时效性和频繁性,可以改为用Redis存储凭证而不是用数据库存储。 ##### **存储用户信息** 同上,每次对网站的请求都会通过拦截器获取用户的登录凭证,然后根据凭证获取用户信息以保持登录状态,访问频率非常高。因此对于findUserById这个方法,有必要做Redis缓存: 这样,整个拦截器所调用的方法就不会涉及到数据库了。 但用户信息的存储会涉及到Redis和MySQL的缓存不一致问题,需要解决: 1.优先从缓存中取值 2.取不到时初始化缓存数据 3.数据变更时清除缓存信息 关于缓存不一致问题,有很多解决方法,这里采用的是:当数据变更时,先更新数据库,再删除缓存。 当然无论是先更新数据库还是先删除缓存,都会有并发访问情况下的不一致问题和第二步操作失败的问题。 前一个问题可以采用延迟双删策略来解决。后一个问题可以用重试机制来解决 ####