27、视频功能(全)

视频功能

一、发布视频

数据库表结构

{
"_id": ObjectId("5e82dd6164019531fc471ff0"),
"vid": NumberLong("100001"),
"userId": NumberLong("3"),
"picUrl": "https://tanhua-dev.oss-cn-zhangjiakou.aliyuncs.com/photo/4/1.jpg",
"videoUrl": "https://tanhua-dev.oss-cn-zhangjiakou.aliyuncs.com/images/video/1576134125940400.mp4",
"created": NumberLong("1585634657964"),
"seeType": NumberInt("1"),
"locationName": "上海市",
"_class": "com.tanhua.dubbo.server.pojo.Video",
"likeCount": 0,
"commentCount": 0,
"loveCount": 0
}

数据模型实体类

package com.tanhua.model.mongo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.bson.types.ObjectId;
import org.springframework.data.mongodb.core.mapping.Document;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "video")
public class Video implements java.io.Serializable {
private static final long serialVersionUID = -3136732836884933873L;
private ObjectId id; // 主键 id
private Long vid; // 自动增长
private Long created; // 创建时间
private Long userId;// 当前用户 id
private String text; // 文字
private String picUrl; // 视频封面文件,URL
private String videoUrl; // 视频文件,URL
private Integer likeCount=0; // 点赞数
private Integer commentCount=0; // 评论数
private Integer loveCount=0; // 喜欢数
}

1. VideoController

package com.tanhua.server.controller;
import com.tanhua.model.domain.PageResult;
import com.tanhua.server.service.SmallVideoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
@RestController
@RequestMapping("/smallVideos")
public class VideoController {
@Autowired
private SmallVideoService smallVideoService;
/**
* 发布视频:
* 请求路径:/smallVideos
* 请求方式:post
* 请求参数:videoThumbnail(视频封面文件),videoFile(视频文件)
*/
@PostMapping
public ResponseEntity uploadSmallVideos(MultipartFile videoThumbnail,MultipartFile videoFile) throws IOException {
smallVideoService.uploadSmallVideos(videoThumbnail,videoFile);
return ResponseEntity.ok(null);
}
}

2.SmallVideoService

package com.tanhua.server.service;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.PageUtil;
import com.github.tobato.fastdfs.domain.conn.FdfsWebServer;
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import com.tanhua.autoconfig.template.OssTemplate;
import com.tanhua.commons.utils.Constants;
import com.tanhua.dubbo.api.FocusUserApi;
import com.tanhua.dubbo.api.UserInfoApi;
import com.tanhua.dubbo.api.VideoApi;
import com.tanhua.model.domain.PageResult;
import com.tanhua.model.domain.UserInfo;
import com.tanhua.model.mongo.FocusUser;
import com.tanhua.model.mongo.Video;
import com.tanhua.model.vo.ErrorResult;
import com.tanhua.model.vo.VideoVo;
import com.tanhua.server.exception.BusinessException;
import com.tanhua.server.interceptor.ThreadLocalUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Service
public class SmallVideoService {
@DubboReference
private VideoApi videoApi;
@DubboReference
private FocusUserApi focusUserApi;
@Autowired
private OssTemplate ossTemplate;
@DubboReference
private UserInfoApi userInfoApi;
@Autowired
private RedisTemplate<String,String> redisTemplate;
@Autowired
private FastFileStorageClient client;
@Autowired
private FdfsWebServer webServer;
/**
* 发布视频
* @param videoThumbnail 视频封面,图片;
* @param videoFile 视频
*/
public void uploadSmallVideos(MultipartFile videoThumbnail, MultipartFile videoFile) throws IOException {
//1. 判断文件或视频是否为空
if(videoThumbnail.isEmpty() || videoFile.isEmpty()){
throw new BusinessException(ErrorResult.error());
}
//2. 图片上传阿里云 oss, 并得到访问路径
String imageUrl = ossTemplate.upload(videoThumbnail.getOriginalFilename(), videoThumbnail.getInputStream());
//3. 视频上传 fastDFS
String filename = videoFile.getOriginalFilename();
// 文件后缀名
filename = filename.substring(filename.lastIndexOf(".") + 1);
StorePath path = client.uploadFile(videoFile.getInputStream(), videoFile.getSize(), filename, null);
String videoUrl = webServer.getWebServerUrl()+path.getFullPath();
//4. 构造 Video 对象封装数据
Video video = new Video();
video.setUserId(ThreadLocalUtils.getUserId());
video.setPicUrl(imageUrl);
video.setVideoUrl(videoUrl);
video.setText("自古忠孝难两全");
//5. 调用 api 保存数据
String videoId = videoApi.save(video);
if(StringUtils.isEmpty(videoId)){
throw new BusinessException(ErrorResult.error());
}
}

3.VideoApiImpl

package com.tanhua.dubbo.api;
import com.tanhua.dubbo.utils.IdWorker;
import com.tanhua.model.mongo.FocusUser;
import com.tanhua.model.mongo.Video;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import java.util.List;
@DubboService
public class VideoApiImpl implements VideoApi {
@Autowired
private MongoTemplate mongoTemplate;
@Autowired
private IdWorker idWorker;
/**
* 发布视频
*/
public String save(Video video) {
//1. 补充缺失的属性
video.setVid(idWorker.getNextId("video"));
video.setCreated(System.currentTimeMillis());
//2. 调用方法保存
mongoTemplate.save(video);
//3. 返回用户 id
return video.getId().toHexString();
}
}

二、查看视频列表

1.VideoController

/**
* 刷视频
* 请求路径:/smallVideos
* 请求方式:get
* 请求参数:page(当前页码),pagesize(每页展示数)
* 响应数据:VideoVo
*/
@GetMapping
public ResponseEntity lookSmallVideos(
@RequestParam(defaultValue = "1") Integer page , @RequestParam(defaultValue = "10") Integer pagesize
){
PageResult pageResult = smallVideoService.lookSmallVideos(page,pagesize);
return ResponseEntity.ok(pageResult);
}

2.SmallVideoService

/**
* 查看小视频列表
* @param page
* @param pagesize
* @return
*/
public PageResult lookSmallVideos(Integer page, Integer pagesize) {
//1. 查看 redis 中是否缓存有大数据推荐系统的推荐 id
Long userId = ThreadLocalUtils.getUserId();
String redisKey = Constants.VIDEOS_RECOMMEND + userId;
String redisValues = redisTemplate.opsForValue().get(redisKey);
int redisPage = 0;
List<Video> videos = new ArrayList<>();
if(!StringUtils.isEmpty(redisValues)){
//2.redis 缓存的数据不为空, 根据 redis 缓存的 vid 分页查询小视频
String[] values = redisValues.split(",");
if(( page-1 )* pagesize < values.length) {
List<Long> vids = Arrays.stream(values).skip((page - 1)* pagesize).limit(pagesize)
.map(e -> Long.valueOf(e))
.collect(Collectors.toList());
videos = videoApi.lookRcommendVideos(vids);
}
redisPage = PageUtil.totalPage(values.length, pagesize);
}
if(CollUtil.isEmpty(videos)){
//3.redis 缓存中没有数据
videos = videoApi.lookVideos(page-redisPage,pagesize);
}
//4. 提取小视频的用户 id,再根据用户 id 查询用户详情
List<Long> userIds = CollUtil.getFieldValues(videos, "userId", Long.class);
Map<Long, UserInfo> userInfoMap = userInfoApi.batchQueryUserInfo(userIds, null);
//5. 遍历 videos 集合,构造 vo 对象
List<VideoVo> vos = new ArrayList<>();
for (Video video : videos) {
Long id = video.getUserId();
UserInfo userInfo = userInfoMap.get(id);
if(userInfo != null){
VideoVo vo = VideoVo.init(userInfo, video);
String key = Constants.FOCUS_USER_KEY + userId;
if(redisTemplate.hasKey(key)){
vo.setHasFocus(1);
}
vos.add(vo);
}
}
return new PageResult(page,pagesize,0,vos);
}

3.VideoApiImpl

/**
* 根据 vid 查
* @param vids
* @return
*/
public List<Video> lookRcommendVideos(List<Long> vids) {
Criteria criteria = Criteria.where("vid").in(vids);
Query query = Query.query(criteria);
List<Video> videos = mongoTemplate.find(query, Video.class);
return videos;
}
/**
* 根据发布时间倒序查
* @param page
* @param pagesize
* @return
*/
public List<Video> lookVideos(int page, Integer pagesize) {
Query query = new Query();
Query query1 = query.skip((page - 1)* pagesize).limit(pagesize).with(Sort.by(Sort.Order.desc("created")));
List<Video> videos = mongoTemplate.find(query1, Video.class);
return videos;
}

三、关注视频发布用户

数据库表结构分析

{
"_id": ObjectId("6364ca6fb25a645456f713f3"),
"userId": NumberLong("1"),
"followUserId": NumberLong("106"),
"created": NumberLong("1667549807183"),
"_class": "com.tanhua.model.mongo.FocusUser"
}

封装数据的实体类

package com.tanhua.model.mongo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.bson.types.ObjectId;
import org.springframework.data.mongodb.core.mapping.Document;
// 用户关注表(关注小视频的发布作者)
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "focus_user")
public class FocusUser implements java.io.Serializable{
private static final long serialVersionUID = 3148619072405056052L;
private ObjectId id; // 主键 id
private Long userId; // 用户 id 106
private Long followUserId; // 关注的用户 id 1
private Long created; // 关注时间
}

1.VideoController

/**
* 关注视频发布用户:
* 请求路径:/smallVideos/:uid/userFocus
* 请求方式:post
* 请求参数:路径参数 uid,要关注得用户 id
*
*/
@PostMapping("/{uid}/userFocus")
public ResponseEntity focusUser(@PathVariable("uid") Long focusUserId){
smallVideoService.focusVideoUser(focusUserId);
return ResponseEntity.ok(null);
}

2.SmallVideoService

/**
* 关注视频发布作者
* @param focusUserId
*/
public void focusVideoUser(Long focusUserId) {
//1. 创建对象封装数据
Long currentUserId = ThreadLocalUtils.getUserId();
FocusUser focusUser = new FocusUser();
focusUser.setUserId(currentUserId);
focusUser.setFollowUserId(focusUserId);
focusUser.setCreated(System.currentTimeMillis());
//2. 把关注的数据缓存进 redis
String key = Constants.FOCUS_USER_KEY + currentUserId;
String hashKey = String.valueOf(focusUserId);
redisTemplate.opsForHash().put(key, hashKey, "1");
//3. 调用 api 保存数据
focusUserApi.save(focusUser);
}

3.FocusUserApiImpl

package com.tanhua.dubbo.api;
import com.tanhua.model.mongo.FocusUser;
import org.apache.dubbo.config.annotation.DubboService;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
@DubboService
public class FocusUserApiImpl implements FocusUserApi {
@Autowired
private MongoTemplate mongoTemplate;
/**
*
* 保存关注用户的数据
*/
public void save(FocusUser focusUser) {
// 保证不重复关注
Criteria criteria =
Criteria.where("userId")
.is(focusUser.getUserId())
.and("followUserId")
.is(focusUser.getFollowUserId());
Query query = Query.query(criteria);
FocusUser user = mongoTemplate.findOne(query, FocusUser.class);
if( user== null){
focusUser.setId(ObjectId.get());
mongoTemplate.save(focusUser);
}
}
}

四、取消关注用户

1.VideoController

/**
* 取消关注
* 请求路径:/smallVideos/:uid/userUnFocus
* 请求方式:post
* 请求参数:uid(要取消关注的用户 id)
*/
@PostMapping("/{uid}/userUnFocus")
public ResponseEntity userUnFocus(@PathVariable("uid") Integer unFocusUserId){
smallVideoService.unFoucus(unFocusUserId);
return ResponseEntity.ok(null);
}

2.SmallVideoController

/**
* 取消关注
* @param unFocusUserId
*/
public void unFoucus(Integer unFocusUserId) {
Long currentUserId = ThreadLocalUtils.getUserId();
//1. 先删除数据库记录
videoApi.delete(currentUserId,unFocusUserId);
//2. 再删除 redis 中的缓存
String key = Constants.FOCUS_USER_KEY + currentUserId;
String hashKey = String.valueOf(unFocusUserId);
redisTemplate.opsForHash().delete(key, hashKey);
}

3.VideoApiImpl

/**
* 取消关注
*/
public void delete(Long currentUserId, Integer unFocusUserId) {
Criteria criteria = Criteria.where("userId").is(currentUserId).and("followUserId").is(unFocusUserId);
Query query = Query.query(criteria);
mongoTemplate.remove(query, FocusUser.class);
}

五、对视频发布评论

数据库表结构

Comment

{
"_id": ObjectId("6365d7c3154d4f5e9de96a55"),
"publishId": ObjectId("6364acefd95af276934d011a"),
// 这里的 publishId 就是 video 表的 Id
"commentType": NumberInt("2"),
"content": "欧文好好打球好吗",
"userId": NumberLong("1"),
"publishUserId": NumberLong("106"),
"created": NumberLong("1667618754738"),
"likeCount": NumberInt("0"),
"_class": "com.tanhua.model.mongo.Comment"
}

Video

{
"_id": ObjectId("5e82dd6264019531fc471ff2"),
"vid": NumberLong("100002"),
"userId": NumberLong("8"),
"picUrl": "https://tanhua-dev.oss-cn-zhangjiakou.aliyuncs.com/photo/10/1564567528297.jpg",
"videoUrl": "https://tanhua-dev.oss-cn-zhangjiakou.aliyuncs.com/images/video/1576134125940400.mp4",
"created": NumberLong("1585634658007"),
"seeType": NumberInt("1"),
"locationName": "上海市",
"_class": "com.tanhua.dubbo.server.pojo.Video",
"likeCount": 0,
"commentCount": 0,
"loveCount": 0
}

vo 对象, 这个是返回给页面的对象

package com.tanhua.model.vo;
import com.tanhua.model.domain.UserInfo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.BeanUtils;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Date;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CommentVo implements Serializable {
private String id; // 评论 id
private String avatar; // 头像
private String nickname; // 昵称
private String content; // 评论
private String createDate; // 评论时间
private Integer likeCount; // 点赞数
private Integer hasLiked; // 是否点赞(1 是,0 否)
public static CommentVo init(UserInfo userInfo, com.tanhua.model.mongo.Comment item) {
CommentVo vo = new CommentVo();
BeanUtils.copyProperties(userInfo, vo);
BeanUtils.copyProperties(item, vo);
vo.setHasLiked(0);
Date date = new Date(item.getCreated());
vo.setCreateDate(new SimpleDateFormat("yyyy-MM-dd HH🇲🇲ss").format(date));
vo.setId(item.getId().toHexString());
return vo;
}
}

1.Controller 接收参数调用 Service 层

/**
* 对视频发布评论:
* 请求路径:/smallVideos/:id/comments
* 请求方式:post
* 请求参数:id(路径参数是被评论的视频 id);comment(评论的正文)
* 响应数据:null
*/
@PostMapping("/{id}/comments")
public ResponseEntity commentVideo(@PathVariable("id") String videoId,
@RequestBody Map map ){
String comment = (String) map.get("comment");
//1. 接收参数调用业务层
smallVideoService.commentVideo(videoId,comment);
return ResponseEntity.ok(null);
}

2.Service 层创建对象封装数据,调用 api 层保存数据

/**
* 对视频发布评论
* @param videoId
* @param comment
*/
public void commentVideo(String videoId, String comment) {
Long currnetUserId = ThreadLocalUtils.getUserId();
//1. 创建 Comment 对象封装数据
Comment videoComment = new Comment();
videoComment.setPublishId(new ObjectId(videoId));// 被评论的视频 Id
videoComment.setCommentType(CommentType.COMMENT.getType());// 是评论?喜欢?还是点赞
videoComment.setContent(comment);// 评论的正文
videoComment.setUserId(currnetUserId);// 评论人的 id
videoComment.setCreated(System.currentTimeMillis());// 评论时间
//2. 调用 api 保存数据
Integer count = commentApi.saveVideoMent(videoComment);
}

3.api,操作 mongoDb 保存数据

/**
* 对视频发布评论
* @param videoComment
* @return
*/
public Integer saveVideoMent(Comment videoComment) {
//1. 根据视频的 id 查询 video 表,获取里面的用户 id 数据, 封装到 videoComment
ObjectId videoId = videoComment.getPublishId();
Video video = mongoTemplate.findById(videoId, Video.class);
videoComment.setPublishUserId(video.getUserId());
//2. 保存到数据库
mongoTemplate.save(videoComment);
//3. 更新 video 表里的 likeCount 或 commentCount 或 loveCount 字段
// 并获取更新后的数据
// 设置查询 id
Criteria criteria = Criteria.where("id").is(videoComment.getPublishId());
Query query = Query.query(criteria);
Update update = new Update();
if(videoComment.getCommentType() == CommentType.LIKE.getType()){
update.inc("likeCount", 1);// 设置要修改的字段以及操作
}else if(videoComment.getCommentType() == CommentType.COMMENT.getType() ){
update.inc("commentCount", 1);
}else {
update.inc("loveCount", 1);
}
FindAndModifyOptions options = new FindAndModifyOptions();
options.returnNew(true);// 得到更新后的数据
Video andModify = mongoTemplate.findAndModify(query, update, options, Video.class);
Integer count = andModify.statisCount(videoComment.getCommentType());
return count;
}

六、查看视频的评论列表

1.Controller

/**
* 视频评论列表:
* 请求路径:/smallVideos/:id/comments
* 请求方式:get
* 请求参数:String id(视频 id),Integer page(当前页码), Integer pagesize(每页显示数)
* 响应数据:CommentVo
*/
@GetMapping("/{id}/comments")
public ResponseEntity lookVideoComment(@PathVariable("id") String videoId,
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer pagesize){
PageResult pageResult = smallVideoService.lookVideoComment(videoId,page,pagesize);
return ResponseEntity.ok(pageResult);
}

2.Service

/**
* 查看视频评论列表
* @param videoId
* @param page
* @param pagesize
* @return
*/
public PageResult lookVideoComment(String videoId, Integer page, Integer pagesize) {
//1. 此处视频的 id 就是 Comment 表中的 publishId 字段
// 根据 publishId 字段查询和 CommentType 字段分页查询 Comment 列表
List<Comment> comments = commentApi.lookVideoComment(videoId,page,pagesize,CommentType.COMMENT);
//2. 如果 comments 为空,n 证明此视频没有评论,new PageResult 返回
if(CollUtil.isEmpty(comments)){
return new PageResult();
}
//3. 提取这些评论的用户 id,再根据用户 id 查询用户详情
List<Long> userIds = CollUtil.getFieldValues(comments, "userId", Long.class);
Map<Long, UserInfo> userInfoMap = userInfoApi.batchQueryUserInfo(userIds, null);
//4. 构造 vo 返回
List<CommentVo> vos = new ArrayList<>();
for (Comment comment : comments) {
Long userId = comment.getUserId();
UserInfo userInfo = userInfoMap.get(userId);
if(userInfo != null){
CommentVo vo = CommentVo.init(userInfo, comment);
String key = Constants.VIDEOCOMMENT_INTERACT_KEY +comment.getId().toHexString();
String hashKey = Constants.VIDEOCOMMENT_LIKE + ThreadLocalUtils.getUserId();
if(redisTemplate.opsForHash().hasKey(key, hashKey)){
vo.setHasLiked(1);
}
vos.add(vo);
}
}
return new PageResult(page,pagesize,0,vos);
}

3.api 层, 操作 mongo

/**
* 查看视频评论列表
* @param videoId
* @param page
* @param pagesize
* @param comment
* @return
*/
public List<Comment> lookVideoComment(String videoId, Integer page, Integer pagesize, CommentType comment) {
//1.
Criteria criteria = Criteria.where("publishId").is(new ObjectId(videoId)).and("commentType").is(comment.getType());
Query query = Query.query(criteria);
query.skip((page - 1)* pagesize).limit(pagesize).with(Sort.by(Sort.Order.desc("created")));
List<Comment> comments = mongoTemplate.find(query, Comment.class);
return comments;
}

七、对视频评论点赞

1.Controller 层

/**
* 视频评论点赞:
* 请求路径:/smallVideos/comments/:id/like
* 请求方式:post
* 请求参数:路径参数,id(评论的 id)
*/
@PostMapping("/comments/{id}/like")
public ResponseEntity videoCommentLike(@PathVariable("id") String videoCommentId){
smallVideoService.videoCommentLike(videoCommentId);
return ResponseEntity.ok(null);
}

2.Service 层

/**
*
* 视频的评论点赞
*/
public void videoCommentLike(String videoCommentId) {
commentApi.videoCommentLike(videoCommentId);
//4. 把点赞状态,缓存到 Redis
// 存的 value 是 hash 值
String key = Constants.VIDEOCOMMENT_INTERACT_KEY +videoCommentId;//comment 的 id
String hashKey = Constants.VIDEOCOMMENT_LIKE + ThreadLocalUtils.getUserId();
redisTemplate.opsForHash().put(key, hashKey, "1");
}

api 层次,操作 mongdb

/**
* 视频的评论点赞
* @param videoCommentId
*/
public void videoCommentLike(String videoCommentId) {
Criteria criteria = Criteria.where("id").is(new ObjectId(videoCommentId));
Query query = Query.query(criteria);
Update update = new Update();
update.inc("likeCount", 1);
mongoTemplate.updateFirst(query, update, Comment.class);
}

八、取消对视频评论的点赞

1.Controller

/**
* 视频评论取消喜欢:
* 请求路径:/smallVideos/comments/:id/dislike
* 请求方式:post
* 请求参数:id(路径参数), 评论 id
*/
@PostMapping("/comments/{id}/dislike")
public ResponseEntity disLikeVideoComment(@PathVariable("id") String videoCommentId){
smallVideoService.disLikeVideoComment(videoCommentId);
return ResponseEntity.ok(null);
}

2.Service 层次

/**
* 视频评论取消喜欢
* @param videoCommentId
*/
public void disLikeVideoComment(String videoCommentId) {
commentApi.disLikeVideoComment(videoCommentId);
// 删除 redis 中缓存的数据
String key = Constants.VIDEOCOMMENT_INTERACT_KEY +videoCommentId;//comment 的 id
String hashKey = Constants.VIDEOCOMMENT_LIKE + ThreadLocalUtils.getUserId();
redisTemplate.opsForHash().delete(key, hashKey);
}

3.api 层

/**
* 取消视频评论的喜欢
* @param videoCommentId
*/
public void disLikeVideoComment(String videoCommentId) {
Criteria criteria = Criteria.where("id").is(new ObjectId(videoCommentId));
Query query = Query.query(criteria);
Update update = new Update();
update.inc("likeCount", -1);
mongoTemplate.findAndModify(query, update,Comment.class);
}

九、对视频点赞

Controller 表现层

/**
* 对视频点赞:
* 请求路径:/smallVideos/:id/like
* 请求方式:post
* 请求参数:id(路径参数), 要点赞的那个视频的 id
* 响应结果:null
*/
@PostMapping("/{id}/like")
public ResponseEntity likeVideo(@PathVariable("id") String videoId){
smallVideoService.likeVideo(videoId);
return ResponseEntity.ok(null);
}

Service

/**
* 对视频点赞
* @param videoId 视频的 id
*/
public void likeVideo(String videoId) {
//1. 先判断是否已经点赞过,点赞过抛出一个异常, 判断的依据是 publishId 和 commentType 以及 userId 字段
// 这里 comment 表里的 publishId 字段就是 video
Long currentUserId = ThreadLocalUtils.getUserId();
Boolean result = commentApi.isLike(currentUserId, videoId, CommentType.LIKE);
if(result){
throw new BusinessException(ErrorResult.error());
}
//2. 创建对象封装数据
Comment comment = new Comment();
comment.setPublishId(new ObjectId(videoId));
comment.setUserId(currentUserId);
comment.setCommentType(CommentType.LIKE.getType());
comment.setCreated(System.currentTimeMillis());
// 被评论人的 id 留到 api 层次封装
Integer count = commentApi.saveVideoMent(comment);
//3. 把对视频的点赞记录缓存进 redis
String key = Constants.VIDEO_INTERACT_KEY +videoId;
String hashKey = Constants.VIDEO_LIKE_HASHKEY + currentUserId;
redisTemplate.opsForHash().put(key, hashKey, "1");
}

api 层

/**
* 查看 comment 表是否有数据,操作的是 Comment 表;
* 依据是 userId、publishId、commentType 这三个字段
*/
public Boolean isLike(Long userId, String movementId, CommentType commentType) {
Criteria criteria = Criteria.where("userId").is(userId)
.and("publishId").is(new ObjectId(movementId))
.and("commentType").is(commentType.getType());
Query query = Query.query(criteria);
boolean result = mongoTemplate.exists(query, Comment.class);
return result;
}
/**
* 对视频发布评论、点赞、喜欢
* @param videoComment
* @return
*/
public Integer saveVideoMent(Comment videoComment) {
//1. 根据视频的 id 查询 video 表,获取里面的用户 id 数据, 封装到 videoComment
ObjectId videoId = videoComment.getPublishId();
Video video = mongoTemplate.findById(videoId, Video.class);
videoComment.setPublishUserId(video.getUserId());
//2. 保存到数据库
mongoTemplate.save(videoComment);
//3. 更新 video 表里的 likeCount 或 commentCount 或 loveCount 字段
// 并获取更新后的数据
// 根据 video 的 id 查询要修改的 video
Criteria criteria = Criteria.where("id").is(videoComment.getPublishId());
Query query = Query.query(criteria);
Update update = new Update();
if(videoComment.getCommentType() == CommentType.LIKE.getType()){
update.inc("likeCount", 1);// 设置要修改的字段以及操作
}else if(videoComment.getCommentType() == CommentType.COMMENT.getType() ){
update.inc("commentCount", 1);
}else {
update.inc("loveCount", 1);
}
FindAndModifyOptions options = new FindAndModifyOptions();
options.returnNew(true);// 得到更新后的数据
Video andModify = mongoTemplate.findAndModify(query, update, options, Video.class);
Integer count = andModify.statisCount(videoComment.getCommentType());
return count;
}

十、取消点赞

Controller

/**
* 取消对视频的点赞
* 请求路径:/smallVideos/:id/dislike
* 请求方式:post
* 请求参数:id(路径参数),要取消点赞视频 id
* 响应数据:null
*/
@PostMapping("/{id}/dislike")
public ResponseEntity disLike(@PathVariable("id") String videoId){
smallVideoService.disLikeVideo(videoId);
return ResponseEntity.ok(null);
}

Service

/**
* 对视频取消点赞
* @param videoId
*/
public void disLikeVideo(String videoId) {
//1. 判断是否点赞过,没点赞怎么会有取消点赞
Long currentUserId = ThreadLocalUtils.getUserId();
Boolean result = commentApi.isLike(currentUserId, videoId, CommentType.LIKE);//true 代表数据库表中有记录,点赞过
if(!result){
// 没点赞,不可以取消, 抛出异常
throw new BusinessException(ErrorResult.error());
}
//2. 封装数据, 调用 api 操作 MongoDB,删除数据
Comment comment = new Comment();
comment.setUserId(ThreadLocalUtils.getUserId());//
comment.setPublishId(new ObjectId(videoId));
comment.setCommentType(CommentType.LIKE.getType());
comment.setCreated(System.currentTimeMillis());
Integer count = commentApi.disLikeVideo(comment);
//3. 删除 redis 中的缓存
String key = Constants.VIDEO_INTERACT_KEY +videoId;
String hashKey = Constants.VIDEO_LIKE_HASHKEY + currentUserId;
redisTemplate.opsForHash().delete(key, hashKey);
}

ApiImpl

/**
* 取消对视频的点赞
* @param comment
* @return
*/
public Integer disLikeVideo(Comment comment) {
//1. 删除 comment 表中的数据
Criteria criteria = Criteria.where("userId").is(comment.getUserId())
.and("publishId").is(comment.getPublishId())
.and("commentType").is(comment.getCommentType());
Query query = Query.query(criteria);
mongoTemplate.remove(query, Comment.class);
//2. 修改 video 表中的点赞、评论、喜欢等数据,并获取更新后的数据
Criteria criteria1 = Criteria.where("id").is(comment.getPublishId());
Query query1 =Query.query(criteria1);
Update update = new Update();
if(comment.getCommentType() == CommentType.LIKE.getType()){
update.inc("likeCount",-1);
}else if (comment.getCommentType() == CommentType.COMMENT.getType()){
update.inc("commentCount", -1);
}else{
update.inc("loveCount", -1);
}
FindAndModifyOptions options = new FindAndModifyOptions();
options.returnNew(true);
Video modify = mongoTemplate.findAndModify(query1, update, options, Video.class);
Integer count = modify.statisCount(comment.getCommentType());
return count;
}