课程说明

  • 圈子实现点赞、喜欢功能
  • 圈子实现评论
  • 圈子实现评论的点赞
  • 小视频功能说明
  • FastDFS环境搭建
  • 小视频的功能实现

1、评论

点赞、喜欢、评论等均可理解为用户对动态的评论。

1.1、点赞

1.1.1、定义dubbo接口

  1. package com.tanhua.dubbo.server.api;
  2. import com.tanhua.dubbo.server.pojo.Pu blish;
  3. import com.tanhua.dubbo.server.vo.PageInfo;
  4. public interface QuanZiApi {
  5. /**
  6. * 发布动态
  7. *
  8. * @param publish
  9. * @return
  10. */
  11. boolean savePublish(Publish publish);
  12. /**
  13. * 查询动态
  14. *
  15. * @return
  16. */
  17. PageInfo<Publish> queryPublishList(Long userId, Integer page, Integer pageSize);
  18. /**
  19. * 点赞
  20. *
  21. * @param userId
  22. * @param publishId
  23. * @return
  24. */
  25. boolean saveLikeComment(Long userId, String publishId);
  26. /**
  27. * 取消点赞、喜欢等
  28. *
  29. * @param userId
  30. * @param publishId
  31. * @return
  32. */
  33. boolean removeComment(Long userId, String publishId, Integer commentType);
  34. /**
  35. * 喜欢
  36. *
  37. * @param userId
  38. * @param publishId
  39. * @return
  40. */
  41. boolean saveLoveComment(Long userId, String publishId);
  42. /**
  43. * 保存评论
  44. *
  45. * @param userId
  46. * @param publishId
  47. * @param type
  48. * @param content
  49. * @return
  50. */
  51. boolean saveComment(Long userId, String publishId, Integer type, String content);
  52. /**
  53. * 查询评论数
  54. *
  55. * @param publishId
  56. * @param type
  57. * @return
  58. */
  59. Long queryCommentCount(String publishId, Integer type);
  60. }

1.1.2、编写实现

  1. package com.tanhua.dubbo.server.api;
  2. import com.alibaba.dubbo.config.annotation.Service;
  3. import com.mongodb.client.result.DeleteResult;
  4. import com.tanhua.dubbo.server.pojo.*;
  5. import com.tanhua.dubbo.server.vo.PageInfo;
  6. import org.apache.commons.lang3.StringUtils;
  7. import org.bson.types.ObjectId;
  8. import org.springframework.beans.factory.annotation.Autowired;
  9. import org.springframework.data.domain.PageRequest;
  10. import org.springframework.data.domain.Sort;
  11. import org.springframework.data.mongodb.core.MongoTemplate;
  12. import org.springframework.data.mongodb.core.query.Criteria;
  13. import org.springframework.data.mongodb.core.query.Query;
  14. import java.util.ArrayList;
  15. import java.util.Date;
  16. import java.util.List;
  17. @Service(version = "1.0.0")
  18. public class QuanZiApiImpl implements QuanZiApi {
  19. @Autowired
  20. private MongoTemplate mongoTemplate;
  21. /**
  22. * 点赞
  23. *
  24. * @param userId
  25. * @param publishId
  26. */
  27. public boolean saveLikeComment(Long userId, String publishId) {
  28. Query query = Query.query(Criteria
  29. .where("publishId").is(new ObjectId(publishId))
  30. .and("userId").is(userId)
  31. .and("commentType").is(1));
  32. long count = this.mongoTemplate.count(query, Comment.class);
  33. if (count > 0) {
  34. return false;
  35. }
  36. return this.saveComment(userId, publishId, 1, null);
  37. }
  38. /**
  39. * 取消点赞、喜欢等
  40. *
  41. * @return
  42. */
  43. public boolean removeComment(Long userId, String publishId, Integer commentType) {
  44. Query query = Query.query(Criteria
  45. .where("publishId").is(new ObjectId(publishId))
  46. .and("userId").is(userId)
  47. .and("commentType").is(commentType));
  48. DeleteResult remove = this.mongoTemplate.remove(query, Comment.class);
  49. return remove.getDeletedCount() > 0;
  50. }
  51. /**
  52. * 喜欢
  53. *
  54. * @param userId
  55. * @param publishId
  56. */
  57. public boolean saveLoveComment(Long userId, String publishId) {
  58. Query query = Query.query(Criteria
  59. .where("publishId").is(new ObjectId(publishId))
  60. .and("userId").is(userId)
  61. .and("commentType").is(3));
  62. long count = this.mongoTemplate.count(query, Comment.class);
  63. if (count > 0) {
  64. return false;
  65. }
  66. return this.saveComment(userId, publishId, 3, null);
  67. }
  68. /**
  69. * 保存评论
  70. *
  71. * @param userId
  72. * @param publishId
  73. * @param type
  74. * @return
  75. */
  76. public boolean saveComment(Long userId, String publishId, Integer type, String content) {
  77. try {
  78. Comment comment = new Comment();
  79. comment.setId(ObjectId.get());
  80. comment.setUserId(userId);
  81. comment.setContent(content);
  82. comment.setPublishId(new ObjectId(publishId));
  83. comment.setCommentType(type);
  84. comment.setCreated(System.currentTimeMillis());
  85. this.mongoTemplate.save(comment);
  86. return true;
  87. } catch (Exception e) {
  88. e.printStackTrace();
  89. }
  90. return false;
  91. }
  92. @Override
  93. public Long queryCommentCount(String publishId, Integer type) {
  94. Query query = Query.query(Criteria.where("publishId").is(new ObjectId(publishId)).and("commentType").is(type));
  95. return this.mongoTemplate.count(query, Comment.class);
  96. }
  97. }

1.1.3、编写接口服务

MovementsController:

  1. /**
  2. * 点赞
  3. *
  4. * @param publishId
  5. * @return
  6. */
  7. @GetMapping("/{id}/like")
  8. public ResponseEntity<Long> likeComment(@PathVariable("id") String publishId) {
  9. try {
  10. Long likeCount = this.movementsService.likeComment(publishId);
  11. if (likeCount != null) {
  12. return ResponseEntity.ok(likeCount);
  13. }
  14. } catch (Exception e) {
  15. e.printStackTrace();
  16. }
  17. return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
  18. }
  19. /**
  20. * 取消点赞
  21. *
  22. * @param publishId
  23. * @return
  24. */
  25. @GetMapping("/{id}/dislike")
  26. public ResponseEntity<Long> disLikeComment(@PathVariable("id") String publishId) {
  27. try {
  28. Long likeCount = this.movementsService.cancelLikeComment(publishId);
  29. if (null != likeCount) {
  30. return ResponseEntity.ok(likeCount);
  31. }
  32. } catch (Exception e) {
  33. e.printStackTrace();
  34. }
  35. return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
  36. }

1.1.4、编写服务实现

  1. package com.tanhua.server.service;
  2. import com.alibaba.dubbo.common.utils.CollectionUtils;
  3. import com.alibaba.dubbo.config.annotation.Reference;
  4. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  5. import com.tanhua.dubbo.server.api.QuanZiApi;
  6. import com.tanhua.dubbo.server.pojo.Publish;
  7. import com.tanhua.dubbo.server.vo.PageInfo;
  8. import com.tanhua.server.pojo.User;
  9. import com.tanhua.server.pojo.UserInfo;
  10. import com.tanhua.server.utils.RelativeDateFormat;
  11. import com.tanhua.server.utils.UserThreadLocal;
  12. import com.tanhua.server.vo.Movements;
  13. import com.tanhua.server.vo.PageResult;
  14. import com.tanhua.server.vo.PicUploadResult;
  15. import org.apache.commons.lang3.StringUtils;
  16. import org.springframework.beans.factory.annotation.Autowired;
  17. import org.springframework.data.redis.core.RedisTemplate;
  18. import org.springframework.stereotype.Service;
  19. import org.springframework.web.multipart.MultipartFile;
  20. import java.util.ArrayList;
  21. import java.util.Date;
  22. import java.util.List;
  23. @Service
  24. public class MovementsService {
  25. @Reference(version = "1.0.0")
  26. private QuanZiApi quanZiApi;
  27. @Autowired
  28. private PicUploadService picUploadService;
  29. @Autowired
  30. private UserInfoService userInfoService;
  31. @Autowired
  32. private RedisTemplate<String, String> redisTemplate;
  33. /**
  34. * 点赞
  35. *
  36. * @param publishId
  37. * @return
  38. */
  39. public Long likeComment(String publishId) {
  40. User user = UserThreadLocal.get();
  41. boolean bool = this.quanZiApi.saveLikeComment(user.getId(), publishId);
  42. if (!bool) {
  43. return null;
  44. }
  45. Long likeCount = 0L;
  46. //保存点赞数到redis
  47. String key = "QUANZI_COMMENT_LIKE_" + publishId;
  48. if (!this.redisTemplate.hasKey(key)) {
  49. Long count = this.quanZiApi.queryCommentCount(publishId, 1);
  50. likeCount = count;
  51. this.redisTemplate.opsForValue().set(key, String.valueOf(likeCount));
  52. } else {
  53. likeCount = this.redisTemplate.opsForValue().increment(key);
  54. }
  55. //记录已点赞
  56. String userKey = "QUANZI_COMMENT_LIKE_USER_" + user.getId() + "_" + publishId;
  57. this.redisTemplate.opsForValue().set(userKey, "1");
  58. return likeCount;
  59. }
  60. /**
  61. * 取消点赞
  62. *
  63. * @return
  64. */
  65. public Long cancelLikeComment(String publishId) {
  66. User user = UserThreadLocal.get();
  67. boolean bool = this.quanZiApi.removeComment(user.getId(), publishId, 1);
  68. if (bool) {
  69. String key = "QUANZI_COMMENT_LIKE_" + publishId;
  70. //数量递减
  71. Long likeCount = this.redisTemplate.opsForValue().decrement(key);
  72. //删除已点赞
  73. String userKey = "QUANZI_COMMENT_LIKE_USER_" + user.getId() + "_" + publishId;
  74. this.redisTemplate.delete(userKey);
  75. return likeCount;
  76. }
  77. return null;
  78. }
  79. }

1.1.5、修改查询动态点赞数

1567998799803.png

1.1.6、测试

1567998836428.png

1.2、喜欢

1.2.1、MovementsController

  1. /**
  2. * 喜欢
  3. *
  4. * @param publishId
  5. * @return
  6. */
  7. @GetMapping("/{id}/love")
  8. public ResponseEntity<Long> loveComment(@PathVariable("id") String publishId) {
  9. try {
  10. Long loveCount = this.movementsService.loveComment(publishId);
  11. if (null != loveCount) {
  12. return ResponseEntity.ok(loveCount);
  13. }
  14. } catch (Exception e) {
  15. e.printStackTrace();
  16. }
  17. return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
  18. }
  19. /**
  20. * 取消喜欢
  21. *
  22. * @param publishId
  23. * @return
  24. */
  25. @GetMapping("/{id}/unlove")
  26. public ResponseEntity<Long> disLoveComment(@PathVariable("id") String publishId) {
  27. try {
  28. Long loveCount = this.movementsService.cancelLoveComment(publishId);
  29. if (null != loveCount) {
  30. return ResponseEntity.ok(loveCount);
  31. }
  32. } catch (Exception e) {
  33. e.printStackTrace();
  34. }
  35. return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
  36. }

1.2.2、MovementsService

  1. /**
  2. * 喜欢
  3. *
  4. * @param publishId
  5. * @return
  6. */
  7. public Long loveComment(String publishId) {
  8. User user = UserThreadLocal.get();
  9. boolean bool = this.quanZiApi.saveLoveComment(user.getId(), publishId);
  10. if (!bool) {
  11. return null;
  12. }
  13. Long loveCount = 0L;
  14. //保存喜欢数到redis
  15. String key = "QUANZI_COMMENT_LOVE_" + publishId;
  16. if (!this.redisTemplate.hasKey(key)) {
  17. Long count = this.quanZiApi.queryCommentCount(publishId, 3);
  18. loveCount = count ;
  19. this.redisTemplate.opsForValue().set(key, String.valueOf(loveCount));
  20. } else {
  21. loveCount = this.redisTemplate.opsForValue().increment(key);
  22. }
  23. //记录已喜欢
  24. String userKey = "QUANZI_COMMENT_LOVE_USER_" + user.getId() + "_" + publishId;
  25. this.redisTemplate.opsForValue().set(userKey, "1");
  26. return loveCount;
  27. }
  28. /**
  29. * 取消喜欢
  30. *
  31. * @return
  32. */
  33. public Long cancelLoveComment(String publishId) {
  34. User user = UserThreadLocal.get();
  35. boolean bool = this.quanZiApi.removeComment(user.getId(), publishId, 3);
  36. if (bool) {
  37. String key = "QUANZI_COMMENT_LOVE_" + publishId;
  38. //数量递减
  39. Long loveCount = this.redisTemplate.opsForValue().decrement(key);
  40. //删除已点赞
  41. String userKey = "QUANZI_COMMENT_LOVE_USER_" + user.getId() + "_" + publishId;
  42. this.redisTemplate.delete(userKey);
  43. return loveCount;
  44. }
  45. return null;
  46. }

1.2.3、测试

1568017597762.png
1568017579434.png

1.3、查询单条动态

1.3.1、定义dubbo接口

QuanZiApi:

  1. /**
  2. * 根据id查询动态
  3. *
  4. * @param id
  5. * @return
  6. */
  7. Publish queryPublishById(String id);

1.3.2、dubbo接口实现

QuanZiApiImpl:

  1. @Override
  2. public Publish queryPublishById(String id) {
  3. return this.mongoTemplate.findById(new ObjectId(id), Publish.class);
  4. }

1.3.3、定义服务接口

  1. /**
  2. * 查询单条动态信息
  3. *
  4. * @param publishId
  5. * @return
  6. */
  7. @GetMapping("/{id}")
  8. public ResponseEntity<Movements> queryById(@PathVariable("id") String publishId) {
  9. try {
  10. Movements movements = this.movementsService.queryById(publishId);
  11. if(null != movements){
  12. return ResponseEntity.ok(movements);
  13. }
  14. } catch (Exception e) {
  15. e.printStackTrace();
  16. }
  17. return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
  18. }

1.3.4、服务实现

  1. public Movements queryById(String publishId) {
  2. Publish publish = this.quanZiApi.queryPublishById(publishId);
  3. if (null == publish) {
  4. return null;
  5. }
  6. Movements movements = new Movements();
  7. movements.setId(publish.getId().toHexString());
  8. movements.setImageContent(publish.getMedias().toArray(new String[]{}));
  9. movements.setTextContent(publish.getText());
  10. movements.setUserId(publish.getUserId());
  11. movements.setCreateDate(RelativeDateFormat.format(new Date(publish.getCreated())));
  12. UserInfo userInfo = this.userInfoService.queryById(publish.getUserId());
  13. if (null == userInfo) {
  14. return null;
  15. }
  16. this.fillValueToMovements(movements, userInfo);
  17. return movements;
  18. }
  19. private void fillValueToMovements(Movements movements, UserInfo userInfo) {
  20. User user = UserThreadLocal.get();
  21. movements.setAge(userInfo.getAge());
  22. movements.setAvatar(userInfo.getLogo());
  23. movements.setGender(userInfo.getSex().name().toLowerCase());
  24. movements.setNickname(userInfo.getNickName());
  25. movements.setTags(StringUtils.split(userInfo.getTags(), ','));
  26. Long commentCount = this.quanZiApi.queryCommentCount(movements.getId(), 2);
  27. if(null == commentCount){
  28. movements.setCommentCount(0); //评论数
  29. }else{
  30. movements.setCommentCount(commentCount.intValue()); //评论数
  31. }
  32. movements.setDistance("1.2公里"); //TODO 距离
  33. String userKey = "QUANZI_COMMENT_LIKE_USER_" + user.getId() + "_" + movements.getId();
  34. movements.setHasLiked(this.redisTemplate.hasKey(userKey) ? 1 : 0); //是否点赞(1是,0否)
  35. String key = "QUANZI_COMMENT_LIKE_" + movements.getId();
  36. String value = this.redisTemplate.opsForValue().get(key);
  37. if (StringUtils.isNotEmpty(value)) {
  38. movements.setLikeCount(Integer.valueOf(value)); //点赞数
  39. } else {
  40. movements.setLikeCount(0);
  41. }
  42. String userLoveKey = "QUANZI_COMMENT_LOVE_USER_" + user.getId() + "_" + movements.getId();
  43. movements.setHasLoved(this.redisTemplate.hasKey(userLoveKey) ? 1 : 0); //是否喜欢(1是,0否)
  44. key = "QUANZI_COMMENT_LOVE_" + movements.getId();
  45. value = this.redisTemplate.opsForValue().get(key);
  46. if (StringUtils.isNotEmpty(value)) {
  47. movements.setLoveCount(Integer.valueOf(value)); //喜欢数
  48. } else {
  49. movements.setLoveCount(0);
  50. }
  51. }

1.3.5、测试

1568119813872.png
1568119828421.png

1.4、单条动态评论

功能包括:查询评论列表,评论点赞、取消点赞。

1.4.1、定义dubbo接口

  1. /**
  2. * 查询单条动态的所有评论
  3. * @param publishId 动态id
  4. * @param page 当前页数
  5. * @param pageSize 每夜显示的条数
  6. * @return 评论数据
  7. */
  8. PageInfo<Comment> queryCommentList(String publishId, Integer page, Integer pageSize);

1.4.2、编写实现

  1. @Override
  2. public PageInfo<Comment> queryCommentList(String publishId, Integer page, Integer pageSize) {
  3. PageRequest pageRequest = PageRequest.of(page - 1, pageSize, Sort.by(Sort.Order.asc("created")));
  4. Query query = new Query(Criteria
  5. .where("publishId").is(new ObjectId(publishId))
  6. .and("commentType").is(2)).with(pageRequest);
  7. //查询时间线表
  8. List<Comment> timeLineList = this.mongoTemplate.find(query, Comment.class);
  9. PageInfo<Comment> pageInfo = new PageInfo<>();
  10. pageInfo.setPageNum(page);
  11. pageInfo.setPageSize(pageSize);
  12. pageInfo.setRecords(timeLineList);
  13. pageInfo.setTotal(0); //不提供总数
  14. return pageInfo;
  15. }

1.4.3、服务接口实现

  1. package com.tanhua.server.vo;
  2. import lombok.AllArgsConstructor;
  3. import lombok.Data;
  4. import lombok.NoArgsConstructor;
  5. /**
  6. * 封装评论的类
  7. */
  8. @Data
  9. @NoArgsConstructor
  10. @AllArgsConstructor
  11. public class Comments {
  12. private String id; //评论id
  13. private String avatar; //头像
  14. private String nickname; //昵称
  15. private String content; //评论
  16. private String createDate; //评论时间: 08:27
  17. private Integer likeCount; //评论的点赞数
  18. private Integer hasLiked; //是否点赞(1是,0否)
  19. }
  1. package com.tanhua.server.controller;
  2. import com.tanhua.server.service.CommentsService;
  3. import com.tanhua.server.service.MovementsService;
  4. import com.tanhua.server.vo.Comments;
  5. import com.tanhua.server.vo.PageResult;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.http.HttpStatus;
  8. import org.springframework.http.ResponseEntity;
  9. import org.springframework.web.bind.annotation.*;
  10. import java.util.Map;
  11. @RestController
  12. @RequestMapping("comments")
  13. public class CommentsController {
  14. @Autowired
  15. private CommentsService commentsService;
  16. @Autowired
  17. private MovementsService movementsService;
  18. /**
  19. * 查询评论列表
  20. *
  21. * @param publishId
  22. * @param page
  23. * @param pagesize
  24. * @return
  25. */
  26. @GetMapping
  27. public ResponseEntity<PageResult> queryCommentsList(@RequestParam("movementId") String publishId,
  28. @RequestParam(value = "page", defaultValue = "1") Integer page,
  29. @RequestParam(value = "pagesize", defaultValue = "10") Integer pagesize) {
  30. try {
  31. PageResult pageResult = this.commentsService.queryCommentsList(publishId, page, pagesize);
  32. return ResponseEntity.ok(pageResult);
  33. } catch (Exception e) {
  34. e.printStackTrace();
  35. }
  36. return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
  37. }
  38. /**
  39. * 保存评论
  40. */
  41. @PostMapping
  42. public ResponseEntity<Void> saveComments(@RequestBody Map<String,String> param) {
  43. try {
  44. String publishId = param.get("movementId");
  45. String content = param.get("comment");
  46. Boolean result = this.commentsService.saveComments(publishId, content);
  47. if (result) {
  48. return ResponseEntity.ok(null);
  49. }
  50. } catch (Exception e) {
  51. e.printStackTrace();
  52. }
  53. return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
  54. }
  55. /**
  56. * 点赞
  57. *
  58. * @param publishId
  59. * @return
  60. */
  61. @GetMapping("/{id}/like")
  62. public ResponseEntity<Long> likeComment(@PathVariable("id") String publishId) {
  63. try {
  64. Long likeCount = this.movementsService.likeComment(publishId);
  65. if (likeCount != null) {
  66. return ResponseEntity.ok(likeCount);
  67. }
  68. } catch (Exception e) {
  69. e.printStackTrace();
  70. }
  71. return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
  72. }
  73. /**
  74. * 取消点赞
  75. *
  76. * @param publishId
  77. * @return
  78. */
  79. @GetMapping("/{id}/dislike")
  80. public ResponseEntity<Long> disLikeComment(@PathVariable("id") String publishId) {
  81. try {
  82. Long likeCount = this.movementsService.cancelLikeComment(publishId);
  83. if (null != likeCount) {
  84. return ResponseEntity.ok(likeCount);
  85. }
  86. } catch (Exception e) {
  87. e.printStackTrace();
  88. }
  89. return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
  90. }
  91. }
  1. package com.tanhua.server.service;
  2. import com.alibaba.dubbo.config.annotation.Reference;
  3. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  4. import com.tanhua.dubbo.server.api.QuanZiApi;
  5. import com.tanhua.dubbo.server.pojo.Comment;
  6. import com.tanhua.dubbo.server.vo.PageInfo;
  7. import com.tanhua.server.pojo.User;
  8. import com.tanhua.server.pojo.UserInfo;
  9. import com.tanhua.server.utils.UserThreadLocal;
  10. import com.tanhua.server.vo.Comments;
  11. import com.tanhua.server.vo.PageResult;
  12. import org.apache.commons.lang3.StringUtils;
  13. import org.joda.time.DateTime;
  14. import org.springframework.beans.factory.annotation.Autowired;
  15. import org.springframework.data.redis.core.RedisTemplate;
  16. import org.springframework.stereotype.Service;
  17. import java.util.ArrayList;
  18. import java.util.List;
  19. @Service
  20. public class CommentsService {
  21. @Reference(version = "1.0.0")
  22. private QuanZiApi quanZiApi;
  23. @Autowired
  24. private UserInfoService userInfoService;
  25. @Autowired
  26. private RedisTemplate<String, String> redisTemplate;
  27. public PageResult queryCommentsList(String publishId, Integer page, Integer pagesize) {
  28. User user = UserThreadLocal.get();
  29. PageInfo<Comment> pageInfo = this.quanZiApi.queryCommentList(publishId, page, pagesize);
  30. List<Comment> records = pageInfo.getRecords();
  31. if (records.isEmpty()) {
  32. PageResult pageResult = new PageResult();
  33. pageResult.setPage(page);
  34. pageResult.setPagesize(pagesize);
  35. pageResult.setPages(0);
  36. pageResult.setCounts(0);
  37. return pageResult;
  38. }
  39. List<Long> userIds = new ArrayList<>();
  40. for (Comment comment : records) {
  41. if (!userIds.contains(comment.getUserId())) {
  42. userIds.add(comment.getUserId());
  43. }
  44. }
  45. QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>();
  46. queryWrapper.in("user_id", userIds);
  47. List<UserInfo> userInfos = this.userInfoService.queryList(queryWrapper);
  48. List<Comments> result = new ArrayList<>();
  49. for (Comment record : records) {
  50. Comments comments = new Comments();
  51. comments.setContent(record.getContent());
  52. comments.setCreateDate(new DateTime(record.getCreated()).toString("yyyy年MM月dd日 HH:mm"));
  53. comments.setId(record.getId().toHexString());
  54. for (UserInfo userInfo : userInfos) {
  55. if (record.getUserId().longValue() == userInfo.getUserId().longValue()) {
  56. comments.setAvatar(userInfo.getLogo());
  57. comments.setNickname(userInfo.getNickName());
  58. break;
  59. }
  60. }
  61. String key = "QUANZI_COMMENT_LIKE_" + comments.getId();
  62. String value = this.redisTemplate.opsForValue().get(key);
  63. if (StringUtils.isNotEmpty(value)) {
  64. comments.setLikeCount(Integer.valueOf(value)); //点赞数
  65. } else {
  66. comments.setLikeCount(0);
  67. }
  68. String userKey = "QUANZI_COMMENT_LIKE_USER_" + user.getId() + "_" + comments.getId();
  69. comments.setHasLiked(this.redisTemplate.hasKey(userKey) ? 1 : 0); //是否点赞(1是,0否)
  70. result.add(comments);
  71. }
  72. PageResult pageResult = new PageResult();
  73. pageResult.setItems(result);
  74. pageResult.setPage(page);
  75. pageResult.setPagesize(pagesize);
  76. pageResult.setPages(0);
  77. pageResult.setCounts(0);
  78. return pageResult;
  79. }
  80. /**
  81. * 保存评论
  82. *
  83. * @param publishId
  84. * @param content
  85. * @return
  86. */
  87. public Boolean saveComments(String publishId, String content) {
  88. User user = UserThreadLocal.get();
  89. return this.quanZiApi.saveComment(user.getId(), publishId, 2, content);
  90. }
  91. }

1.4.4、测试

1568209335129.png
1568209381544.png

2、小视频功能说明

小视频功能类似于抖音、快手小视频的应用,用户可以上传小视频进行分享,也可以浏览查看别人分享的视频,并且可以对视频评论和点赞操作。

效果:
1568878011614.png

查看详情:
1568878327076.png

评论:
1568878353854.png

点赞:
1568878397710.png

3、技术方案

对于小视频的功能的开发,核心点就是:存储 + 推荐 + 加载速度 。

  • 对于存储而言,小视频的存储量以及容量都是非常巨大的
    • 所以我们选择自己搭建分布式存储系统 FastDFS进行存储
  • 对于推荐算法,我们将采用多种权重的计算方式进行计算
  • 对于加载速度,除了提升服务器带宽外可以通过CDN的方式进行加速,当然了这需要额外购买CDN服务

3.1、FastDFS

3.1.1、FastDFS是什么?

FastDFS是分布式文件系统。使用 FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。

3.1.2、工作原理

FastDFS 架构包括 Tracker server 和 Storage server。客户端请求 Tracker server 进行文件上传、下载,通过 Tracker server 调度最终由 Storage server 完成文件上传和下载。

Tracker server 作用是负载均衡和调度,通过 Tracker server 在文件上传时可以根据一些策略找到 Storage server 提供文件上传服务。可以将 tracker 称为追踪服务器或调度服务器。

Storage server 作用是文件存储,客户端上传的文件最终存储在 Storage 服务器上,Storage server 没有实现自己的文件系统而是利用操作系统的文件系统来管理文件。可以将storage称为存储服务器。
6bd41a6af1fef5cc89dda7c94208c6a64f3.png

每个 tracker 节点地位平等。收集 Storage 集群的状态。

Storage 分为多个组,每个组之间保存的文件是不同的。每个组内部可以有多个成员,组成员内部保存的内容是一样的,组成员的地位是一致的,没有主从的概念。

3.1.3、文件的上传

1746338-20190925190927228-1815093216.png

客户端上传文件后存储服务器将文件 ID 返回给客户端,此文件 ID 用于以后访问该文件的索引信息。文件索引信息包括:组名,虚拟磁盘路径,数据两级目录,文件名。

3.1.4、文件的下载

b3d48e8fd6255834f2445e0b956390455a4.png

客户端下载请求到Tracker服务,Tracker返回给客户端storage的信息,客户端根据这些信息进行请求storage获取到文件。

4、FastDFS环境搭建

4.1、搭建服务

我们使用docker进行搭建。

  1. #拉取镜像
  2. docker pull delron/fastdfs
  3. #创建tracker容器
  4. docker create --network=host --name tracker --restart=always -v fdfs-tracker:/var/fdfs delron/fastdfs tracker
  5. #启动容器
  6. docker start tracker
  7. #创建storage容器
  8. docker create --network=host --name storage --restart=always -e TRACKER_SERVER=192.168.31.81:22122 -v fdfs-storage:/var/fdfs -e GROUP_NAME=group1 delron/fastdfs storage
  9. #启动容器
  10. docker start storage
  11. #进入storage容器,到storage的配置文件中配置http访问的端口,配置文件在/etc/fdfs目录下的storage.conf。
  12. docker exec -it storage /bin/bash
  13. #默认的http端口为8888,可以修改也可以配置
  14. # the port of the web server on this storage server
  15. http.server_port=8888
  16. #配置nginx,在/usr/local/nginx目录下,修改nginx.conf文件
  17. #默认配置如下:
  18. server {
  19. listen 8888;
  20. server_name localhost;
  21. location ~/group[0-9]/ {
  22. ngx_fastdfs_module;
  23. }
  24. error_page 500 502 503 504 /50x.html;
  25. location = /50x.html {
  26. root html;
  27. }
  28. }
  29. #默认的存储路径为/var/fdfs/data

1568942782857.png

4.2、java client

导入依赖:导在my-tanhua-server项目中

  1. <dependency>
  2. <groupId>com.github.tobato</groupId>
  3. <artifactId>fastdfs-client</artifactId>
  4. <version>1.26.7</version>
  5. <exclusions>
  6. <exclusion>
  7. <groupId>ch.qos.logback</groupId>
  8. <artifactId>logback-classic</artifactId>
  9. </exclusion>
  10. </exclusions>
  11. </dependency>

4.2.1、application.properties

  1. # ===================================================================
  2. # 分布式文件系统FDFS配置
  3. # ===================================================================
  4. fdfs.so-timeout = 1501
  5. fdfs.connect-timeout = 601
  6. #缩略图生成参数
  7. fdfs.thumb-image.width= 150
  8. fdfs.thumb-image.height= 150
  9. #TrackerList参数,支持多个
  10. fdfs.tracker-list=192.168.31.81:22122
  11. #访问路径
  12. fdfs.web-server-url=http://192.168.31.81:8888/

4.2.2、测试

  1. package com.tanhua.server;
  2. import com.github.tobato.fastdfs.domain.fdfs.StorePath;
  3. import com.github.tobato.fastdfs.service.FastFileStorageClient;
  4. import org.apache.commons.io.FileUtils;
  5. import org.junit.Test;
  6. import org.junit.runner.RunWith;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.boot.test.context.SpringBootTest;
  9. import org.springframework.test.context.junit4.SpringRunner;
  10. import java.io.File;
  11. import java.io.IOException;
  12. @RunWith(SpringRunner.class)
  13. @SpringBootTest
  14. public class TestFastDFS {
  15. @Autowired
  16. protected FastFileStorageClient storageClient;
  17. @Test
  18. public void testUpload(){
  19. String path = "C:\\Users\\zhijun\\Desktop\\pics\\1.jpg";
  20. File file = new File(path);
  21. try {
  22. StorePath storePath = this.storageClient.uploadFile(FileUtils.openInputStream(file), file.length(), "jpg", null);
  23. System.out.println(storePath); //StorePath [group=group1, path=M00/00/00/wKgfUV2GJSuAOUd_AAHnjh7KpOc1.1.jpg]
  24. System.out.println(storePath.getFullPath());//group1/M00/00/00/wKgfUV2GJSuAOUd_AAHnjh7KpOc1.1.jpg
  25. } catch (IOException e) {
  26. e.printStackTrace();
  27. }
  28. }
  29. }

5、发布小视频

5.1、编写pojo

在dubbo接口工程中编写pojo:

  1. package com.tanhua.dubbo.server.pojo;
  2. import lombok.AllArgsConstructor;
  3. import lombok.Data;
  4. import lombok.NoArgsConstructor;
  5. import org.bson.types.ObjectId;
  6. import org.springframework.data.mongodb.core.mapping.Document;
  7. import java.util.List;
  8. @Data
  9. @NoArgsConstructor
  10. @AllArgsConstructor
  11. @Document(collection = "video")
  12. public class Video implements java.io.Serializable {
  13. private static final long serialVersionUID = -3136732836884933873L;
  14. private ObjectId id; //主键id
  15. private Long userId;
  16. private String text; //文字
  17. private String picUrl; //视频封面文件
  18. private String videoUrl; //视频文件
  19. private Long created; //创建时间
  20. private Integer seeType; // 谁可以看,1-公开,2-私密,3-部分可见,4-不给谁看
  21. private List<Long> seeList; //部分可见的列表
  22. private List<Long> notSeeList; //不给谁看的列表
  23. private String longitude; //经度
  24. private String latitude; //纬度
  25. private String locationName; //位置名称
  26. }

5.2、定义接口

  1. package com.tanhua.dubbo.server.api;
  2. import com.tanhua.dubbo.server.pojo.Video;
  3. public interface VideoApi {
  4. /**
  5. * 保存小视频
  6. *
  7. * @param video
  8. * @return
  9. */
  10. Boolean saveVideo(Video video);
  11. }

5.3、实现

  1. package com.tanhua.dubbo.server.api;
  2. import com.alibaba.dubbo.config.annotation.Service;
  3. import com.tanhua.dubbo.server.pojo.Video;
  4. import org.bson.types.ObjectId;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.data.mongodb.core.MongoTemplate;
  7. @Service(version = "1.0.0")
  8. public class VideoApiImpl implements VideoApi {
  9. @Autowired
  10. private MongoTemplate mongoTemplate;
  11. @Override
  12. public Boolean saveVideo(Video video) {
  13. if(video.getUserId() == null){
  14. return false;
  15. }
  16. video.setId(ObjectId.get());
  17. video.setCreated(System.currentTimeMillis());
  18. this.mongoTemplate.save(video);
  19. return true;
  20. }
  21. }

5.4、接口服务

1569074668308.png

5.4.1、VideoController

  1. package com.tanhua.server.controller;
  2. import com.tanhua.server.service.VideoService;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.http.HttpStatus;
  5. import org.springframework.http.ResponseEntity;
  6. import org.springframework.web.bind.annotation.PostMapping;
  7. import org.springframework.web.bind.annotation.RequestMapping;
  8. import org.springframework.web.bind.annotation.RequestParam;
  9. import org.springframework.web.bind.annotation.RestController;
  10. import org.springframework.web.multipart.MultipartFile;
  11. @RestController
  12. @RequestMapping("smallVideos")
  13. public class VideoController {
  14. @Autowired
  15. private VideoService videoService;
  16. @PostMapping
  17. public ResponseEntity<Void> saveVideo(@RequestParam(value = "videoThumbnail", required = false) MultipartFile picFile,
  18. @RequestParam(value = "videoFile", required = false) MultipartFile videoFile) {
  19. try {
  20. Boolean bool = this.videoService.saveVideo(picFile, videoFile);
  21. if(bool){
  22. return ResponseEntity.ok(null);
  23. }
  24. } catch (Exception e) {
  25. e.printStackTrace();
  26. }
  27. return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
  28. }
  29. }

5.4.2、VideoService

  1. package com.tanhua.server.service;
  2. import com.alibaba.dubbo.config.annotation.Reference;
  3. import com.github.tobato.fastdfs.domain.conn.FdfsWebServer;
  4. import com.github.tobato.fastdfs.domain.fdfs.StorePath;
  5. import com.github.tobato.fastdfs.service.FastFileStorageClient;
  6. import com.tanhua.dubbo.server.api.VideoApi;
  7. import com.tanhua.dubbo.server.pojo.Video;
  8. import com.tanhua.server.pojo.User;
  9. import com.tanhua.server.utils.UserThreadLocal;
  10. import com.tanhua.server.vo.PicUploadResult;
  11. import org.apache.commons.lang3.StringUtils;
  12. import org.springframework.beans.factory.annotation.Autowired;
  13. import org.springframework.stereotype.Service;
  14. import org.springframework.web.multipart.MultipartFile;
  15. @Service
  16. public class VideoService {
  17. @Autowired
  18. private PicUploadService picUploadService;
  19. @Autowired
  20. protected FastFileStorageClient storageClient;
  21. @Autowired
  22. private FdfsWebServer fdfsWebServer;
  23. @Reference(version = "1.0.0")
  24. private VideoApi videoApi;
  25. public Boolean saveVideo(MultipartFile picFile, MultipartFile videoFile) {
  26. User user = UserThreadLocal.get();
  27. Video video = new Video();
  28. video.setUserId(user.getId());
  29. video.setSeeType(1);
  30. try {
  31. //上传封面图片
  32. PicUploadResult picUploadResult = this.picUploadService.upload(picFile);
  33. video.setPicUrl(picUploadResult.getName()); //图片路径
  34. //上传视频
  35. StorePath storePath = storageClient.uploadFile(videoFile.getInputStream(),
  36. videoFile.getSize(),
  37. StringUtils.substringAfter(videoFile.getOriginalFilename(), "."),
  38. null);
  39. video.setVideoUrl(fdfsWebServer.getWebServerUrl() + "/" + storePath.getFullPath());
  40. this.videoApi.saveVideo(video);
  41. return true;
  42. } catch (Exception e) {
  43. e.printStackTrace();
  44. }
  45. return false;
  46. }
  47. }

5.4.3、测试

如果上传视频,会导致异常,是因为请求太大的缘故:
1569075180126.png

解决:application.properties

  1. spring.servlet.multipart.max-file-size=30MB
  2. spring.servlet.multipart.max-request-size=30MB

问题解决:
1569075257357.png

6、小视频列表

6.1、定义dubbo服务

  1. package com.tanhua.dubbo.server.api;
  2. import com.tanhua.dubbo.server.pojo.Video;
  3. import com.tanhua.dubbo.server.vo.PageInfo;
  4. public interface VideoApi {
  5. /**
  6. * 保存小视频
  7. *
  8. * @param video
  9. * @return
  10. */
  11. Boolean saveVideo(Video video);
  12. /**
  13. * 分页查询小视频列表,按照时间倒序排序
  14. *
  15. * @param page
  16. * @param pageSize
  17. * @return
  18. */
  19. PageInfo<Video> queryVideoList(Integer page, Integer pageSize);
  20. }

6.2、实现dubbo服务

  1. package com.tanhua.dubbo.server.api;
  2. import com.alibaba.dubbo.config.annotation.Service;
  3. import com.tanhua.dubbo.server.pojo.Video;
  4. import com.tanhua.dubbo.server.vo.PageInfo;
  5. import org.bson.types.ObjectId;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.data.domain.PageRequest;
  8. import org.springframework.data.domain.Pageable;
  9. import org.springframework.data.domain.Sort;
  10. import org.springframework.data.mongodb.core.MongoTemplate;
  11. import org.springframework.data.mongodb.core.query.Query;
  12. import java.util.List;
  13. @Service(version = "1.0.0")
  14. public class VideoApiImpl implements VideoApi {
  15. @Autowired
  16. private MongoTemplate mongoTemplate;
  17. @Override
  18. public Boolean saveVideo(Video video) {
  19. if (video.getUserId() == null) {
  20. return false;
  21. }
  22. video.setId(ObjectId.get());
  23. video.setCreated(System.currentTimeMillis());
  24. this.mongoTemplate.save(video);
  25. return null;
  26. }
  27. @Override
  28. public PageInfo<Video> queryVideoList(Integer page, Integer pageSize) {
  29. Pageable pageable = PageRequest.of(page - 1, pageSize, Sort.by(Sort.Order.desc("created")));
  30. Query query = new Query().with(pageable);
  31. List<Video> videos = this.mongoTemplate.find(query, Video.class);
  32. PageInfo<Video> pageInfo = new PageInfo<>();
  33. pageInfo.setRecords(videos);
  34. pageInfo.setPageNum(page);
  35. pageInfo.setPageSize(pageSize);
  36. pageInfo.setTotal(0); //不提供总数
  37. return pageInfo;
  38. }
  39. }

6.3、定义VideoVo

  1. package com.tanhua.server.vo;
  2. import lombok.AllArgsConstructor;
  3. import lombok.Data;
  4. import lombok.NoArgsConstructor;
  5. @Data
  6. @NoArgsConstructor
  7. @AllArgsConstructor
  8. public class VideoVo {
  9. private String id;
  10. private Long userId;
  11. private String avatar; //头像
  12. private String nickname; //昵称
  13. private String cover; //封面
  14. private String videoUrl; //视频URL
  15. private String signature; //签名
  16. private Integer likeCount; //点赞数量
  17. private Integer hasLiked; //是否已赞(1是,0否)
  18. private Integer hasFocus; //是是否关注 (1是,0否)
  19. private Integer commentCount; //评论数量
  20. }

6.4、VideoController

  1. @RestController
  2. @RequestMapping("smallVideos")
  3. public class VideoController {
  4. /**
  5. * 查询小视频列表
  6. *
  7. * @param page
  8. * @param pageSize
  9. * @return
  10. */
  11. @GetMapping
  12. public ResponseEntity<PageResult> queryVideoList(@RequestParam(value = "page", defaultValue = "1") Integer page,
  13. @RequestParam(value = "pagesize", defaultValue = "10") Integer pageSize) {
  14. try {
  15. if (page <= 0) {
  16. page = 1;
  17. }
  18. PageResult pageResult = this.videoService.queryVideoList(page, pageSize);
  19. if (null != pageResult) {
  20. return ResponseEntity.ok(pageResult);
  21. }
  22. } catch (Exception e) {
  23. e.printStackTrace();
  24. }
  25. return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
  26. }
  27. }

6.5、VideoService

  1. public PageResult queryVideoList(Integer page, Integer pageSize) {
  2. User user = UserThreadLocal.get();
  3. PageResult pageResult = new PageResult();
  4. pageResult.setPage(page);
  5. pageResult.setPagesize(pageSize);
  6. pageResult.setPages(0);
  7. pageResult.setCounts(0);
  8. PageInfo<Video> pageInfo = this.videoApi.queryVideoList(page, pageSize);
  9. List<Video> records = pageInfo.getRecords();
  10. List<VideoVo> videoVoList = new ArrayList<>();
  11. List<Long> userIds = new ArrayList<>();
  12. for (Video record : records) {
  13. VideoVo videoVo = new VideoVo();
  14. videoVo.setUserId(record.getUserId());
  15. videoVo.setCover(record.getPicUrl());
  16. videoVo.setVideoUrl(record.getVideoUrl());
  17. videoVo.setId(record.getId().toHexString());
  18. videoVo.setSignature("我就是我~");
  19. Long commentCount = this.quanZiApi.queryCommentCount(videoVo.getId(), 2);
  20. videoVo.setCommentCount(commentCount == null ? 0 : commentCount.intValue()); // 评论数
  21. String followUserKey = "VIDEO_FOLLOW_USER_" + user.getId() + "_" + videoVo.getUserId();
  22. videoVo.setHasFocus(this.redisTemplate.hasKey(followUserKey) ? 1 : 0); //是否关注
  23. String userKey = "QUANZI_COMMENT_LIKE_USER_" + user.getId() + "_" + videoVo.getId();
  24. videoVo.setHasLiked(this.redisTemplate.hasKey(userKey) ? 1 : 0); //是否点赞(1是,0否)
  25. String key = "QUANZI_COMMENT_LIKE_" + videoVo.getId();
  26. String value = this.redisTemplate.opsForValue().get(key);
  27. if (StringUtils.isNotEmpty(value)) {
  28. videoVo.setLikeCount(Integer.valueOf(value)); //点赞数
  29. } else {
  30. videoVo.setLikeCount(0);
  31. }
  32. if (!userIds.contains(record.getUserId())) {
  33. userIds.add(record.getUserId());
  34. }
  35. videoVoList.add(videoVo);
  36. }
  37. QueryWrapper<UserInfo> queryWrapper = new QueryWrapper();
  38. queryWrapper.in("user_id", userIds);
  39. List<UserInfo> userInfos = this.userInfoService.queryList(queryWrapper);
  40. for (VideoVo videoVo : videoVoList) {
  41. for (UserInfo userInfo : userInfos) {
  42. if (videoVo.getUserId().longValue() == userInfo.getUserId().longValue()) {
  43. videoVo.setNickname(userInfo.getNickName());
  44. videoVo.setAvatar(userInfo.getLogo());
  45. break;
  46. }
  47. }
  48. }
  49. pageResult.setItems(videoVoList);
  50. return pageResult;
  51. }

6.6、测试

day05-圈子、小视频功能实现 - 图20

day05-圈子、小视频功能实现 - 图21

7、视频点赞

点赞逻辑与之前的圈子点赞一样,所以实现也是一样的。

mock地址:https://mock.boxuegu.com/project/164/interface/api/77987

day05-圈子、小视频功能实现 - 图22

VideoController:

  1. /**
  2. * 视频点赞
  3. *
  4. * @param videoId 视频id
  5. * @return
  6. */
  7. @PostMapping("/{id}/like")
  8. public ResponseEntity<Long> likeComment(@PathVariable("id") String videoId) {
  9. return this.movementsController.likeComment(videoId);
  10. }
  11. /**
  12. * 取消点赞
  13. *
  14. * @param videoId
  15. * @return
  16. */
  17. @PostMapping("/{id}/dislike")
  18. public ResponseEntity<Long> disLikeComment(@PathVariable("id") String videoId) {
  19. return this.movementsController.disLikeComment(videoId);
  20. }

8、小视频评论

小视频的评论与圈子的评论逻辑类似,所以也可以使用同一套逻辑进行实现。

8.1、评论列表

mock地址:https://mock.boxuegu.com/project/164/interface/api/72177

  1. /**
  2. * 评论列表
  3. */
  4. @GetMapping("/{id}/comments")
  5. public ResponseEntity<PageResult> queryCommentsList(@PathVariable("id") String videoId,
  6. @RequestParam(value = "page", defaultValue = "1") Integer page,
  7. @RequestParam(value = "pagesize", defaultValue = "10") Integer pagesize) {
  8. return this.commentsController.queryCommentsList(videoId, page, pagesize);
  9. }

8.2、发布评论

mock地址:https://mock.boxuegu.com/project/164/interface/api/72919

  1. /**
  2. * 提交评论
  3. *
  4. * @param param
  5. * @param videoId
  6. * @return
  7. */
  8. @PostMapping("/{id}/comments")
  9. public ResponseEntity<Void> saveComments(@RequestBody Map<String, String> param,
  10. @PathVariable("id") String videoId) {
  11. param.put("movementId", videoId);
  12. return this.commentsController.saveComments(param);
  13. }

8.3、评论点赞

mock地址:https://mock.boxuegu.com/project/164/interface/api/75306

  1. /**
  2. * 评论点赞
  3. *
  4. * @param publishId
  5. * @return
  6. */
  7. @PostMapping("/comments/{id}/like")
  8. public ResponseEntity<Long> commentsLikeComment(@PathVariable("id") String publishId) {
  9. return this.movementsController.likeComment(publishId);
  10. }
  11. /**
  12. * 评论取消点赞
  13. *
  14. * @param publishId
  15. * @return
  16. */
  17. @PostMapping("/comments/{id}/dislike")
  18. public ResponseEntity<Long> disCommentsLikeComment(@PathVariable("id") String publishId) {
  19. return this.movementsController.disLikeComment(publishId);
  20. }

8.4、关注用户

关注用户是关注小视频发布的作者,这样我们后面计算推荐时,关注的用户将权重更重一些。

8.4.1、定义dubbo服务

  1. package com.tanhua.dubbo.server.pojo;
  2. import lombok.AllArgsConstructor;
  3. import lombok.Data;
  4. import lombok.NoArgsConstructor;
  5. import org.bson.types.ObjectId;
  6. import org.springframework.data.mongodb.core.mapping.Document;
  7. @Data
  8. @NoArgsConstructor
  9. @AllArgsConstructor
  10. @Document(collection = "follow_user")
  11. public class FollowUser implements java.io.Serializable{
  12. private static final long serialVersionUID = 3148619072405056052L;
  13. private ObjectId id; //主键id
  14. private Long userId; //用户id
  15. private Long followUserId; //关注的用户id
  16. private Long created; //关注时间
  17. }
  1. package com.tanhua.dubbo.server.api;
  2. import com.tanhua.dubbo.server.pojo.Video;
  3. import com.tanhua.dubbo.server.vo.PageInfo;
  4. public interface VideoApi {
  5. /**
  6. * 保存小视频
  7. *
  8. * @param video
  9. * @return
  10. */
  11. Boolean saveVideo(Video video);
  12. /**
  13. * 分页查询小视频列表,按照时间倒序排序
  14. *
  15. * @param page
  16. * @param pageSize
  17. * @return
  18. */
  19. PageInfo<Video> queryVideoList(Integer page, Integer pageSize);
  20. /**
  21. * 关注用户
  22. *
  23. * @param userId
  24. * @param followUserId
  25. * @return
  26. */
  27. Boolean followUser(Long userId, Long followUserId);
  28. /**
  29. * 取消关注用户
  30. *
  31. * @param userId
  32. * @param followUserId
  33. * @return
  34. */
  35. Boolean disFollowUser(Long userId, Long followUserId);
  36. }

接口实现:

  1. //VideoApiImpl:
  2. @Override
  3. public Boolean followUser(Long userId, Long followUserId) {
  4. try {
  5. FollowUser followUser = new FollowUser();
  6. followUser.setId(ObjectId.get());
  7. followUser.setUserId(userId);
  8. followUser.setFollowUserId(followUserId);
  9. followUser.setCreated(System.currentTimeMillis());
  10. this.mongoTemplate.save(followUser);
  11. return true;
  12. } catch (Exception e) {
  13. e.printStackTrace();
  14. }
  15. return false;
  16. }
  17. @Override
  18. public Boolean disFollowUser(Long userId, Long followUserId) {
  19. Query query = Query.query(Criteria.where("userId").is(userId).and("followUserId").is(followUserId));
  20. DeleteResult deleteResult = this.mongoTemplate.remove(query, FollowUser.class);
  21. return deleteResult.getDeletedCount() > 0;
  22. }

8.4.2、服务实现

  1. //VideoController:
  2. /**
  3. * 视频用户关注
  4. */
  5. @PostMapping("/{id}/userFocus")
  6. public ResponseEntity<Void> saveUserFocusComments(@PathVariable("id") Long userId) {
  7. try {
  8. Boolean bool = this.videoService.followUser(userId);
  9. if (bool) {
  10. return ResponseEntity.ok(null);
  11. }
  12. } catch (Exception e) {
  13. e.printStackTrace();
  14. }
  15. return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
  16. }
  17. /**
  18. * 取消视频用户关注
  19. */
  20. @PostMapping("/{id}/userUnFocus")
  21. public ResponseEntity<Void> saveUserUnFocusComments(@PathVariable("id") Long userId) {
  22. try {
  23. Boolean bool = this.videoService.disFollowUser(userId);
  24. if (bool) {
  25. return ResponseEntity.ok(null);
  26. }
  27. } catch (Exception e) {
  28. e.printStackTrace();
  29. }
  30. return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
  31. }
  1. //VideoService:
  2. /**
  3. * 关注用户
  4. *
  5. * @param userId
  6. * @return
  7. */
  8. public Boolean followUser(Long userId) {
  9. User user = UserThreadLocal.get();
  10. this.videoApi.followUser(user.getId(), userId);
  11. //记录已关注
  12. String followUserKey = "VIDEO_FOLLOW_USER_" + user.getId() + "_" + userId;
  13. this.redisTemplate.opsForValue().set(followUserKey, "1");
  14. return true;
  15. }
  16. /**
  17. * 取消关注
  18. *
  19. * @param userId
  20. * @return
  21. */
  22. public Boolean disFollowUser(Long userId) {
  23. User user = UserThreadLocal.get();
  24. this.videoApi.disFollowUser(user.getId(), userId);
  25. String followUserKey = "VIDEO_FOLLOW_USER_" + user.getId() + "_" + userId;
  26. this.redisTemplate.delete(followUserKey);
  27. return true;
  28. }