课程说明
- 圈子动态查询
- 圈子实现评论
- 圈子实现点赞、喜欢功能
- 圈子实现评论
1、动态查询
我的动态:查询个人发布的动态列表(分页查询),和之前实现的好友动态,推荐动态实现逻辑是一致。
1.1、查询好友动态
查询好友动态与查询推荐动态显示的结构是一样的,只是其查询数据源不同
1.1.1、接口文档
API接口文档:http://192.168.136.160:3000/project/19/interface/api/142
1.1.2、代码步骤
- Controller层接受请求参数
- Service数据封装
- 调用API查询好友动态详情数据
- 调用API查询动态发布人详情
- 构造VO对象
- API层根据用户ID查询好友发布动态详情
- 查询好友时间线表
- 查询动态详情
1.1.3、代码实现
MovementController
1. /** 2. * 查询好友动态 3. */ 4. @GetMapping 5. public ResponseEntity movements(@RequestParam(defaultValue = "1") Integer page, 6. @RequestParam(defaultValue = "10") Integer pagesize) { 7. PageResult pr = movementService.findFriendMovements(page,pagesize); 8. return ResponseEntity.ok(pr); 9. }
MovementService
1. //好友动态 2. public PageResult findFriendMovements(Integer page, Integer pagesize) { 3. //1、获取当前用户id 4. Long userId = UserHolder.getUserId(); 5. //2、查询数据列表 6. List<Movement> items = movementApi.findFriendMovements(userId,page,pagesize); 7. //3、非空判断 8. if(CollUtil.isEmpty(items)) { 9. return new PageResult(); 10. } 11. //4、获取好友用户id 12. List<Long> userIds = CollUtil.getFieldValues(items, "userId", Long.class); 13. //5、循环数据列表 14. Map<Long, UserInfo> userMaps = userInfoApi.findByIds(userIds, null); 15. List<MovementsVo> vos = new ArrayList<>(); 16. for (Movement item : items) { 17. //5、一个Movement构建一个Vo对象 18. UserInfo userInfo = userMaps.get(item.getUserId()); 19. MovementsVo vo = MovementsVo.init(userInfo, item); 20. vos.add(vo); 21. } 22. //6、构建返回值 23. return new PageResult(page,pagesize,0L,vos); 24. }
movementApi
1. @Override 2. public List<Movement> findFriendMovements(Long friendId, Integer page, Integer pagesize) { 3. //1、查询好友时间线表 4. Query query = Query.query(Criteria.where("friendId").in(friendId)) 5. .skip((page - 1)*pagesize).limit(pagesize) 6. .with(Sort.by(Sort.Order.desc("created"))); 7. List<MovementTimeLine> lines = mongoTemplate.find(query, MovementTimeLine.class); 8. //2、提取动态id集合 9. List<ObjectId> movementIds = CollUtil.getFieldValues(lines, "movementId", ObjectId.class); 10. //3、根据动态id查询动态详情 11. Query movementQuery = Query.query(Criteria.where("id").in(movementIds)); 12. return mongoTemplate.find(movementQuery, Movement.class); 13. }
1.2、查询推荐动态
推荐动态是通过推荐系统计算出的结果,现在我们只需要实现查询即可,推荐系统在后面的课程中完成。
推荐系统计算完成后,会将结果数据写入到Redis中,数据如下:
1. 192.168.31.81:6379> get MOVEMENTS_RECOMMEND_1 2. "2562,3639,2063,3448,2128,2597,2893,2333,3330,2642,2541,3002,3561,3649,2384,2504,3397,2843,2341,2249"
可以看到,在Redis中的数据是有多个发布id组成(pid)由逗号分隔。所以实现中需要自己对这些数据做分页处理。
1.2.1、接口文档
API接口文档:http://192.168.136.160:3000/project/19/interface/api/145
1.2.2、代码步骤
- Controller层接受请求参数
- Service数据封装
- 从redis获取当前用户的推荐PID列表
- 如果不存在,调用API随机获取10条动态数据
- 如果存在,调用API根据PID列表查询动态数据
- 构造VO对象
- API层编写方法
- 随机获取
- 根据pid列表查询
1.2.3、代码实现
Constants
1. package com.tanhua.commons.utils; 2. 3. //常量定义 4. public class Constants { 5. 6. //手机APP短信验证码CHECK_CODE_ 7. public static final String SMS_CODE = "CHECK_CODE_"; 8. 9. //推荐动态 10. public static final String MOVEMENTS_RECOMMEND = "MOVEMENTS_RECOMMEND_"; 11. 12. //推荐视频 13. public static final String VIDEOS_RECOMMEND = "VIDEOS_RECOMMEND_"; 14. 15. //圈子互动KEY 16. public static final String MOVEMENTS_INTERACT_KEY = "MOVEMENTS_INTERACT_"; 17. 18. //动态点赞用户HashKey 19. public static final String MOVEMENT_LIKE_HASHKEY = "MOVEMENT_LIKE_"; 20. 21. //动态喜欢用户HashKey 22. public static final String MOVEMENT_LOVE_HASHKEY = "MOVEMENT_LOVE_"; 23. 24. //视频点赞用户HashKey 25. public static final String VIDEO_LIKE_HASHKEY = "VIDEO_LIKE"; 26. 27. //访问用户 28. public static final String VISITORS = "VISITORS"; 29. 30. //关注用户 31. public static final String FOCUS_USER = "FOCUS_USER_{}_{}"; 32. 33. //初始化密码 34. public static final String INIT_PASSWORD = "123456"; 35. 36. //环信用户前缀 37. public static final String HX_USER_PREFIX = "hx"; 38. 39. //jwt加密盐 40. public static final String JWT_SECRET = "itcast"; 41. 42. //jwt超时时间 43. public static final int JWT_TIME_OUT = 3_600; 44. }
MovementController
1. /** 2. * 推荐动态 3. */ 4. @GetMapping("/recommend") 5. public ResponseEntity recommend(@RequestParam(defaultValue = "1") Integer page, 6. @RequestParam(defaultValue = "10") Integer pagesize) { 7. PageResult pr = movementService.findRecommendMovements(page,pagesize); 8. return ResponseEntity.ok(pr); 9. }
MovementService
1. //推荐动态 2. public PageResult findRecommendMovements(Integer page, Integer pagesize) { 3. String redisKey = "MOVEMENTS_RECOMMEND_" + UserHolder.getUserId(); 4. String redisData = this.redisTemplate.opsForValue().get(redisKey); 5. List<Movement> list = Collections.EMPTY_LIST; 6. if(StringUtils.isEmpty(redisData)){ 7. list = movementApi.randomMovements(pagesize); 8. }else { 9. String[] split = redisData.split(","); 10. if ((page-1) * pagesize > split.length) { 11. return new PageResult(); 12. } 13. List<Long> pids = Arrays.stream(split) 14. .skip((page - 1) * pagesize) 15. .limit(pagesize) 16. .map(e -> Convert.toLong(e)) 17. .collect(Collectors.toList()); 18. list = movementApi.findByPids(pids); 19. } 20. List<Long> userIds = CollUtil.getFieldValues(list, "userId", Long.class); 21. //5、循环数据列表 22. Map<Long, UserInfo> userMaps = userInfoApi.findByIds(userIds, null); 23. List<MovementsVo> vos = new ArrayList<>(); 24. for (Movement item : list) { 25. //5、一个Movement构建一个Vo对象 26. UserInfo userInfo = userMaps.get(item.getUserId()); 27. MovementsVo vo = MovementsVo.init(userInfo, item); 28. vos.add(vo); 29. } 30. //6、构建返回值 31. return new PageResult(page,pagesize,0L,vos); 32. }
movementApi
1. //随机获取 2. public List<Movement> randomMovements(Integer counts) { 3. 4. TypedAggregation aggregation = Aggregation.newAggregation(Movement.class, 5. Aggregation.sample(counts)); 6. 7. AggregationResults<Movement> movements = mongoTemplate.aggregate(aggregation,Movement.class); 8. 9. return movements.getMappedResults(); 10. } 11. 12. 13. //根据PID查询 14. public List<Movement> findByPids(List<Long> pids) { 15. Query query = Query.query(Criteria.where("pId").in(pids)); 16. return mongoTemplate.find(query, Movement.class); 17. }
1.3、根据id查询动态
根据id查询动态:当手机端查看评论内容时(需要根据动态id,查询动态详情),后续再去查询评论列表
1.3.1、接口文档
API接口文档:http://192.168.136.160:3000/project/19/interface/api/151
1.3.2、代码实现
MovementController
1. /** 2. * 根据id查询动态 3. */ 4. @GetMapping("/{id}") 5. public ResponseEntity findById(@PathVariable("id") String movementId) { 6. MovementsVo vo = movementService.findMovementById(movementId); 7. return ResponseEntity.ok(vo); 8. }
MovementService
1. public MovementsVo findMovementById(String movementId) { 2. Movement movements = movementApi.findById(movementId); 3. if(movements == null) { 4. return null; 5. }else { 6. UserInfo userInfo = userInfoApi.findById(movements.getUserId()); 7. return MovementsVo.init(userInfo,movements); 8. } 9. }
movementApi
1. @Override 2. public Movement findById(String movementId) { 3. return mongoTemplate.findById(movementId,Movement.class); 4. }
2、圈子互动
点赞、喜欢、评论等均可理解为用户对动态的互动。
数据库表:quanzi_comment
1. 将数据记录到表中:保存到MongoDB中 2. 互动表需要几张:需要一张表即可(quanzi_comment) 3. 里面的数据需要分类:通过字段commentType 1-点赞,2-评论,3-喜欢 4. { 5. "_id" : ObjectId("5fe7f9263c851428107cd4e8"), 6. "publishId" : ObjectId("5fae53947e52992e78a3afa5"), 7. "commentType" : 1, 8. "userId" : NumberLong(1), 9. "publishUserId" : NumberLong(1), 10. "created" : NumberLong(1609038118275), 11. "likeCount" : 0, 12. "_class" : "com.tanhua.domain.mongo.Comment" 13. }
数据存储位置:redis,mongodb
mongodb中的数据
- 在动态详情Movement表中,加入喜欢,点赞,评论数量:检查数据库访问压力
- 互动操作的时候,不要忘记对上面的字段进行维护
- 圈子互动的表 comment
- 互动完成(点赞,喜欢):不仅要将数据保存到mongo中,需要记录到redis中
- 页面查询圈子列表时,可以从redis中判断是否有点赞,和喜欢历史
2.1、环境搭建
2.1.1 创建API接口
1. public interface CommentApi { 2. 3. }
2.1.2 创建API实现类
1. @DubboService 2. public class CommentApiImpl implements CommentApi { 3. 4. @Autowired 5. private MongoTemplate mongoTemplate; 6. }
2.1.3 Movement对象
1. @Data 2. @NoArgsConstructor 3. @AllArgsConstructor 4. @Document(collection = "movement") 5. public class Movement implements java.io.Serializable { 6. 7. private ObjectId id; //主键id 8. //redis实现,使用Mongodb实现 9. private Long pid; //Long类型,用于推荐系统的模型(自动增长) 10. private Long created; //发布时间 11. private Long userId; 12. private String textContent; //文字 13. private List<String> medias; //媒体数据,图片或小视频 url 14. private String longitude; //经度 15. private String latitude; //纬度 16. private String locationName; //位置名称 17. private Integer state = 0;//状态 0:未审(默认),1:通过,2:驳回 18. 19. //补充字段 20. private Integer likeCount = 0; //点赞数 21. private Integer commentCount = 0; //评论数 22. private Integer loveCount = 0; //喜欢数 23. 24. //根据评论类型,获取对应的互动数量 25. public Integer statisCount(Integer commentType) { 26. if (commentType == CommentType.LIKE.getType()) { 27. return this.likeCount; 28. } else if (commentType == CommentType.COMMENT.getType()) { 29. return this.commentCount; 30. } else { 31. return loveCount; 32. } 33. } 34. }
2.1.4 实体类对象
1. package com.tanhua.domain.mongo; 2. 3. import lombok.AllArgsConstructor; 4. import lombok.Data; 5. import lombok.NoArgsConstructor; 6. import org.bson.types.ObjectId; 7. import org.springframework.data.mongodb.core.mapping.Document; 8. 9. /** 10. * 圈子互动表(点赞,评论,喜欢) 11. */ 12. @Data 13. @NoArgsConstructor 14. @AllArgsConstructor 15. @Document(collection = "comment") 16. public class Comment implements java.io.Serializable{ 17. 18. private ObjectId id; 19. private ObjectId publishId; //发布id 20. private Integer commentType; //评论类型,1-点赞,2-评论,3-喜欢 21. private String content; //评论内容 22. private Long userId; //评论人 23. private Long publishUserId; //被评论人ID 24. private Long created; //发表时间 25. private Integer likeCount = 0; //当前评论的点赞数 26. 27. }
2.1.5 VO对象
1. @Data 2. @NoArgsConstructor 3. @AllArgsConstructor 4. public class CommentVo implements Serializable { 5. 6. private String id; //评论id 7. private String avatar; //头像 8. private String nickname; //昵称 9. 10. 11. private String content; //评论 12. private String createDate; //评论时间 13. private Integer likeCount; //点赞数 14. private Integer hasLiked; //是否点赞(1是,0否) 15. 16. public static CommentVo init(UserInfo userInfo, Comment item) { 17. CommentVo vo = new CommentVo(); 18. BeanUtils.copyProperties(userInfo, vo); 19. BeanUtils.copyProperties(item, vo); 20. vo.setHasLiked(0); 21. Date date = new Date(item.getCreated()); 22. vo.setCreateDate(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date)); 23. vo.setId(item.getId().toHexString()); 24. return vo; 25. } 26. }
2.1.6 CommentType枚举
1. /** 2. * 评论类型:1-点赞,2-评论,3-喜欢 3. */ 4. public enum CommentType { 5. 6. LIKE(1), COMMENT(2), LOVE(3); 7. 8. int type; 9. 10. CommentType(int type) { 11. this.type = type; 12. } 13. 14. public int getType() { 15. return type; 16. } 17. }