19.圈子互动
圈子互动
点赞、喜欢、评论等均可理解为用户对动态的互动。
mongodb中的数据
- 在动态详情Movement表中,加入喜欢,点赞,评论数量:减少数据库访问压力
- 互动操作的时候,不要忘记对上面的字段进行维护
- 圈子互动的表 comment
- 互动完成(点赞,喜欢):不仅要将数据保存到mongo中,需要记录到redis中
- 页面查询圈子列表时,可以从redis中判断是否有点赞,和喜欢历史
二、环境搭建
在动态详情表中补充字段
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "movement")
public class Movement implements java.io.Serializable {
private ObjectId id; //主键id
//redis实现,使用Mongodb实现
private Long pid; //Long类型,用于推荐系统的模型(自动增长)
private Long created; //发布时间
private Long userId;
private String textContent; //文字
private List<String> medias; //媒体数据,图片或小视频 url
private String longitude; //经度
private String latitude; //纬度
private String locationName; //位置名称
private Integer state = 0;//状态 0:未审(默认),1:通过,2:驳回
//补充字段
private Integer likeCount = 0; //点赞数
private Integer commentCount = 0; //评论数
private Integer loveCount = 0; //喜欢数
//根据评论类型,获取对应的互动数量
public Integer statisCount(Integer commentType) {
if (commentType == CommentType.LIKE.getType()) {
return this.likeCount;
} else if (commentType == CommentType.COMMENT.getType()) {
return this.commentCount;
} else {
return loveCount;
}
}
}
实体类
Comment
package com.tanhua.domain.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 = "comment")
public class Comment implements java.io.Serializable{
private ObjectId id;
private ObjectId publishId; //发布id
private Integer commentType; //评论类型,1-点赞,2-评论,3-喜欢
private String content; //评论内容
private Long userId; //评论人
private Long publishUserId; //被评论人ID
private Long created; //发表时间
private Integer likeCount = 0; //当前评论的点赞数
}
vo
@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, 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:mm:ss").format(date));
vo.setId(item.getId().toHexString());
return vo;
}
}
CommentType枚举
/**
* 评论类型:1-点赞,2-评论,3-喜欢
*/
public enum CommentType {
LIKE(1), COMMENT(2), LOVE(3);
int type;
CommentType(int type) {
this.type = type;
}
public int getType() {
return type;
}
}
api接口
public interface CommentApi {
}
api实现类
@DubboService
public class CommentApiImpl implements CommentApi {
@Autowired
private MongoTemplate mongoTemplate;
}
三、动态评论
功能包括:查询评论列表,发布评论,对评论点赞和取消点赞。
1. 发布评论
Controller
/**
* 对动态发布评论:
* 请求路径:/comments
* 请求方式:post
* 请求参数:movementId(动态id,string),comment(评论内容,string)
*响应数据:null
*/
@PostMapping
public ResponseEntity postCommen(@RequestBody Map map){
String movementId = (String) map.get("movementId");
String comment = (String) map.get("comment");
commentService.postCommon(movementId,comment);
return ResponseEntity.ok(null);
}
Service
/**
*对动态发布评论
* 就是要向数据库添加一条评论的数据,所有先用对象封装数据,然后调用api保存数据
*/
public void postCommon(String movementId, String text) {
//1.封装数据
/**
* private ObjectId publishId; //要评论的动态id
* private Integer commentType; //评论类型,1-点赞,2-评论,3-喜欢
* private String content; //评论内容
* private Long userId; //评论人的id
* private Long publishUserId; //被评论人ID
* private Long created; //发表时间
* private Integer likeCount = 0; //当前评论的点赞数
*/
//1.封装数据
Comment comment = new Comment();
comment.setPublishId(new ObjectId(movementId));//动态id
comment.setCommentType(CommentType.COMMENT.getType());//评论
comment.setContent(text);//评论的内容
comment.setUserId(ThreadLocalUtils.getUserId());//评论人的id
comment.setCreated(System.currentTimeMillis());
//被评论人的id留到api层再封装
//2.调用api保存数据
Integer integer = commentApi.postCommen(comment);
}
api层
/**
*
* 发布评论
*/
public Integer postCommen(Comment comment) {
//1.接收的comment对象还没有被评论人的用户id
//1.1先根据动态id获取动态详情,再在动态详情里获取被评论人的用户id,最后封装到comment里
ObjectId publishId = comment.getPublishId();
Movement movement = mongoTemplate.findById(publishId, Movement.class);
comment.setPublishUserId(movement.getUserId());
//1.2保存comment到数据库
mongoTemplate.save(comment);
//2.更新动态详情表里面的喜欢、评论、点赞等字段的数据,并获取更新后的数据
//2.1创建Criteria对象,设置查询的条件,根据动态id查询
Criteria criteria = Criteria.where("id").is(comment.getPublishId());
Query query = Query.query(criteria);
//2.2设置要更新的字段
Update update = new Update();
//2.3判断当前要处理的评论类型
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);//获取更新后的数据
Movement modify = mongoTemplate.findAndModify(query, update, options, Movement.class);
Integer count = modify.statisCount(comment.getCommentType());
return count;
}
2.分页列表查询评论
controller
/**
* 分页查询评论:
* * 请求路径:/comments
* * 请求方式:get
* * 请求参数:movementId(动态id),page(当前页),pageSize(每页展示数)
* *响应数据:PageResult
*/
@GetMapping
public ResponseEntity lookComments(String movementId,
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer pageSize){
PageResult pageResult = commentService.lookComments(movementId,page,pageSize);
return ResponseEntity.ok(pageResult);
}
Service
/**
*
*查看评论列表
*/
public PageResult lookComments(String movementId, Integer page, Integer pageSize) {
//1.调用api查询评论comments集合
List<Comment> comments = commentApi.lookComments(movementId, page, pageSize, CommentType.COMMENT.getType());
//2.判断集合是否为空,为空则直接new PageResult返回
if(CollUtil.isEmpty(comments)){
return new PageResult();
}
//3.获取评论人的id,查询评论人的用户详情
List<Long> userIds = CollUtil.getFieldValues(comments, "userId", Long.class);
Map<Long, UserInfo> userInfoMap = userInfoApi.batchQueryUserInfo(userIds, null);
//4.遍历评论集合,一个comment对象就构造一个vo对象
List<CommentVo> commentVos = new ArrayList<>();
for (Comment comment : comments) {
Long id = comment.getUserId();
UserInfo userInfo = userInfoMap.get(id);
if(userInfo != null){
CommentVo commentVo = CommentVo.init(userInfo, comment);
commentVos.add(commentVo);
}
}
//5.构造返回值
PageResult pageResult = new PageResult(page,pageSize,0,commentVos);
return pageResult;
}
api
/**
*
* 查看评论列表
*/
public List<Comment> lookComments(String movementId, Integer page, Integer pageSize, int type) {
//1.创建Criteria对象,设置查询条件,根据动态id和评论类型查询
//注意,参数类型保持一致,条件有两个
Criteria criteria = Criteria.where("publishId").is(new ObjectId(movementId)).and("commentType").is(type);
//2.根据criteria创建query对象
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;
}
3.动态点赞
MovementController
/**
* 动态点赞:
* 请求路径:/movements/:id/like
* 请求方式:get
* 请求参数:路径参数id,动态id
* 响应数据: integer
*/
@GetMapping({"/{id}/like"})
public ResponseEntity like(@PathVariable("id") String id){
Integer integer = commentService.like(id);
return ResponseEntity.ok(integer);
}
CommentService
/**
* 动态点赞
**/
public Integer like(String movementId) {
//1.判断用户是否点赞过,如果有,抛出一个异常
Boolean result = commentApi.isLike(ThreadLocalUtils.getUserId(), movementId,CommentType.LIKE);
//2.true代表点赞过
if(result){
throw new BusinessException(ErrorResult.likeError());
}
//3.创建comment对象封装数据,调用api保存数据
Comment comment = new Comment();
comment.setPublishId(new ObjectId(movementId));//动态id
comment.setCommentType(CommentType.LIKE.getType());
comment.setUserId(ThreadLocalUtils.getUserId());//当前操作的用户id
comment.setCreated(System.currentTimeMillis());
Integer count = commentApi.postCommen(comment);
//4.把点赞状态,缓存到Redis
//存的value是hash值
String key = Constants.MOVEMENTS_INTERACT_KEY +movementId;
String hashKey = Constants.MOVEMENT_LIKE_HASHKEY + ThreadLocalUtils.getUserId();
redisTemplate.opsForHash().put(key, hashKey, "1");
return count;
}
api
/**
*查看comment表是否有数据,取消点赞
*/
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;
}
4.取消点赞
MovementController
/**取消点赞:
* 请求路径:/movements/:id/dislike
* 请求方式:get
* 请求参数:路径参数id,动态id
* 响应数据: Integer
*/
@GetMapping("/{id}/dislike")
public ResponseEntity disLike(@PathVariable("id") String id){
Integer count = commentService.disLikeComment(id);
return ResponseEntity.ok(count);
}
CommentService
/**
*取消点赞
*/
public Integer disLikeComment(String movementId) {
//1.判断用户是否已经点赞过
Boolean result = commentApi.isLike(ThreadLocalUtils.getUserId(),movementId,CommentType.LIKE);
//2.如果没点赞过,抛出一个异常
if(!result){
//true代表点赞过
throw new BusinessException(ErrorResult.disLikeError());
}
//3.封装要删除的数据,调用api删除
Comment comment = new Comment();
comment.setPublishId(new ObjectId(movementId));
comment.setUserId(ThreadLocalUtils.getUserId());
comment.setCommentType(CommentType.LIKE.getType());
comment.setCreated(System.currentTimeMillis());
Integer count = commentApi.delete(comment);
//4.删除redis缓存的点赞状态
String key = Constants.MOVEMENTS_INTERACT_KEY + movementId;
String hashKey = Constants.MOVEMENT_LIKE_HASHKEY + ThreadLocalUtils.getUserId();
redisTemplate.opsForHash().delete(key, hashKey);
return count;
}
5.动态喜欢
Controller
/**
* 动态喜欢:
* 请求路径:/movements/:id/love
* 请求方式:get
* 请求参数:路径参数id(动态id)
* 响应数据: Integer
*/
@GetMapping("/{id}/love")
public ResponseEntity loveMovement(@PathVariable("id") String movementId){
Integer count = commentService.saveLoveMovement(movementId);
return ResponseEntity.ok(count);
}
Service
/**
*
* 动态喜欢
*/
public Integer saveLoveMovement(String movementId) {
//1.判断用户是否点过喜欢,点过之后就不可以再点了;其实际就是查询comment表有没有数据
Boolean result = commentApi.isLike(ThreadLocalUtils.getUserId(),movementId,CommentType.LOVE);
//2.用户点过喜欢,抛出一个异常
if(result){
//true代表用户已喜欢
throw new BusinessException(ErrorResult.loveError());
}
//3.封装数据,调用api保存数据
Comment comment = new Comment();
comment.setPublishId(new ObjectId(movementId));
comment.setCommentType(CommentType.LOVE.getType());
comment.setUserId(ThreadLocalUtils.getUserId());
comment.setCreated(System.currentTimeMillis());
Integer count = commentApi.postCommen(comment);
//4.把喜欢的状态缓存至redis
String key = Constants.MOVEMENTS_INTERACT_KEY + comment.getPublishId();
String hashKey = Constants.MOVEMENT_LOVE_HASHKEY + ThreadLocalUtils.getUserId();
redisTemplate.opsForHash().hasKey(key, hashKey);
return count;
}
6.取消喜欢动态
Controller
/**
* 动态取消喜欢:
* 请求路径:/movements/:id/unlove
* 请求方式:get
* 请求参数:路径参数id(动态Id)
* 响应数据: Integer
*/
@GetMapping("/{id}/unlove")
public ResponseEntity unLove(@PathVariable("id") String movementId){
Integer count = commentService.unLOve(movementId);
return ResponseEntity.ok(count);
}
Service
/**
*取消喜欢
*/
public Integer unLOve(String movementId) {
//2.判断用户有没有喜欢过,没有喜欢过不能取消喜欢
Boolean result = commentApi.isLike(ThreadLocalUtils.getUserId(), movementId, CommentType.LOVE);
if(!result){
throw new BusinessException(ErrorResult.disloveError());
}
//2.封装要删除的数据,调用api删除数据
Comment comment = new Comment();
comment.setPublishId(new ObjectId(movementId));
comment.setCommentType(CommentType.LOVE.getType());
comment.setUserId(ThreadLocalUtils.getUserId());
comment.setCreated(System.currentTimeMillis());
Integer count = commentApi.delete(comment);
//3.删除redis缓存的喜欢状态
String key = Constants.MOVEMENTS_INTERACT_KEY + comment.getPublishId();
String hashKey = Constants.MOVEMENT_LOVE_HASHKEY + ThreadLocalUtils.getUserId();
redisTemplate.opsForHash().delete(key, hashKey);
return count;
}