项目面.md
项目面
[toc]
乐享视频
一、背景和参与人
此项目为在校兼职项目,帮助学长学姐完成毕设,成交价格2300;
共3人参与项目,前端vue,后端java,一人协助上线,我主要负责后端框架搭建,数据库设计,思路整理。
二、项目介绍
说到项目,其实是一个视频学习网站,整体设计参考B站;分为管理端和用户端,分免费和会员两类视频;基本实现视频管理/观看。会员充值/购买,用户评论/管理,笔记记录,点赞收藏等功能;整合第三方短信验证码服务,整合minio文件存储服务;最终实现线上部署及域名访问。
三、主要技术栈
- spring boot
- mysql数据库
- mybatis plus持久层框架
- java过滤器 + redis 权限认证
- 阿里短信服务 + redis 实现验证码注册登录
- lombok简化实体
- swagger实时更新api文档
- slf4j+logback日志打印
四、详解数据库设计
- 所有表均记录id、创建时间、更新时间
- user用户表:除了账号密码等常用字段外,因为涉及管理员/用户端区分,添加
类型
字段;因为涉及会员/非会员之分,添加是否会员
字段;因为需要模拟用户虚拟钱包支付,添加余额
字段 - video视频表:主要字段有:主图路径(text)、视频路径(text)、类型;其次标注课程
是否会员
,记录点击次数
;记录上下架
状态 - reply回复表:暂为一级评论:主要标记
视频id
;标记回复人id
;回复内容 - 其他较为简单的表:banner轮播图表,blog笔记表,collect收藏表等,主要均为标记视频与业务间关系
五、详解技术实现
java过滤器 + redis 权限认证:redis常用场景之一,此处键值设计为{token : userInfo},过期时间2小时;
- 首先在用户登录时:
- 若可以通过账号密码查询到用户信息,也即登录成功;
- 生成唯一uuid作为token,当前用户信息转为json字符串;分别作为键值存入redis;
- 将生成的uuid存放如response的header中返回前端
- 前端请求接口时:
- request的header携带登录获取的token,请求后端接口
- 用户过滤器:
- 新建类实现spring的HandlerInterceptor接口
- 重写preHandle预处理方法,获取前端携带的token去redis查找用户信息
- 用户信息为空,说明用户退出或者token过期,限制访问;用户信息存在,放行接口,继续业务操作;
- 注册/配置过滤器
- 新建配置类并使用@Configuration标注为配置类
- 新建WebMvcConfigurer方法,并使用@Bean注解,使其替换自带的mvc配置
- 方法中配置所有接口后,放行部分公共路径,如:登录,注册接口
阿里短信服务 + redis 实现验证码注册登录:第二个redis常用场景,此处键值设计为{tel : code},若有多个业务可标注接口名称如:{login_tel:code};有效时间5分钟
- 用户点击获取验证码:
- 首先在后台生成随机六位数字
- 通过阿里短信sdk请求其接口,若返回结果ok,继续后续操作;否则抛出异常
- 将用户手机号、生成的随机六位数字分别作为键值存入redis
- 用户登录/注册:
- 若用户使用验证码登录,需校验验证码code正确性
- 通过用户手机号从redis中获取redis值:redisCode
- 将用户输入的code与redisCode对比,若相同,登录/注册成功,否则失败
六、你在项目中遇到的难点,如何解决
首先是如何鉴权:见 五、java过滤器 + redis 权限认证
其次是多级评论设计:解决方式为:
- 在评论表中新增父级评论id
p_id
默认值0 - 一级列表展示:只需要查询所有
p_id=0
的评论列表 - 用户点击展开某个评论:只需要通过父级id查询子级评论即可
- 树形评论列表:只需要对评论列表进行递归查询子级,递归结束条件为其子级为null
- 参考代码:https://blog.csdn.net/tomisa/article/details/107933980
- 在评论表中新增父级评论id
类似的还有权限列表递归
- 数据库设计
- 返回实体结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21@Data
public class PermissionTreeVO {
/** 主键 */
private Long id;
/** 权限名 */
private String perName;
/** 权限路径 */
private String perUrl;
/** 权限类型 例如 0 菜单 1按钮 */
private Integer perType;
/** 父级权限 默认 0 */
private Long parentId;
/** 图标 */
private String icon;
/** 状态 0 禁用 1 启用 */
private Boolean status;
/** 描述 */
private String remark;
/** 子权限 */
private List<PermissionTreeVO> childs;
} - 具体实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45//controller
/**
* 通过parentId递归查询权限树(查询全部传0)
*/
@ApiOperation(value = "通过parentId递归查询权限树(查询全部传0)")
@PostMapping("/umsPermission/selectPermissionTreeByParentId/{parentId}")
public Result selectPermissionTreeByParentId(@PathVariable Long parentId) {
List<PermissionTreeVO> permissionTreeVOS = umsPermissionService.selectPermissionTreeByParentId(parentId);
return Result.succeed(permissionTreeVOS, "查询成功");
}
// serviceImpl
/**
* @Description: 以输入的父级id寻找下级目录,只要下级目录不为空,就继续向下探寻,然后封装至上级的childs字段中
* @param parentId
* @return
*/
@Override
public List<PermissionTreeVO> selectPermissionTreeByParentId(Long parentId) {
List<PermissionTreeVO> permissionList = umsPermissionMapper.selectPermissionTreeByParentId(parentId);
if(permissionList!=null){
for (PermissionTreeVO permission : permissionList) {
permission.setChilds(selectPermissionTreeByParentId(permission.getId()));
}
}
return permissionList;
}
// mapper.xml
<select id="selectPermissionTreeByParentId" resultType="com.yk.i_wms.vo.PermissionTreeVO" parameterType="long">
SELECT DISTINCT
t.id id,
t.per_name perName,
t.per_url perUrl,
t.per_type perType,
t.icon icon,
t.status status,
t.remark remark,
t.parent_id parentId
FROM
ums_permission t
WHERE
t.parent_id = #{parentId}
order by t.id
</select>
- 数据库设计
七、主要接口
注册:选择管理员或用户注册,可使用短信验证码注册,如此将生成默认密码;
登录:自动确定其为管理员还是用户;分短信验证码登录和密码登录,存储过程见 五、java过滤器 + redis 权限认证
退出:删除2中redis的token即可
发送验证码:见 五、阿里短信服务
文件上传:使用minio存储,项目引入minio-spring boot.jar;使用其sdk进行文件上传;并返回公网链接
会员充值:用户表:扣减余额并修改会员状态
评论:记录视频id,当前用户id,上级评论id
评论查询:见六、多级评论设计
收藏/收藏数:记录视频id,当前用户id;查询视频列表时count收藏数量
某用户是否收藏某视频的判断:用户点击视频,获取视频id和用户id,进行收藏表查询,若存在记录且收藏状态为已收藏;前端点亮收藏按钮,否则置灰收藏按钮
八、扩展
- MessageUtil/MsgController:短信服务工具类/接口
- MinioUtil/FileController:文件工具类/接口
- RedisUtils:redis 常用工具类
- LogAspect:日志切面,使用spring的aop思想,注解:@Aspect 对接口进行入参、出参、请求方式和路径的记录
- GlobalExceptionHandler:全局统一异常处理:使用注解@ControllerAdvice标注类,使用@ExceptionHandler标注方法进行异常捕捉
- SwaggerConfig:swagger配置
- Result:统一结果返回,使用静态方法简化结果返回