实现评论、小视频

评论

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

点赞

定义dubbo接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
package com.tanhua.dubbo.server.api;

import com.tanhua.dubbo.server.pojo.Comment;
import com.tanhua.dubbo.server.pojo.Publish;
import com.tanhua.dubbo.server.vo.PageInfo;

public interface QuanZiApi {

/**
* 发布动态
*
* @param publish
* @return
*/
boolean savePublish(Publish publish);

/**
* 查询好友动态
*
* @param userId
* @param page
* @param pageSize
* @return
*/
PageInfo<Publish> queryPublishList(Long userId, Integer page, Integer pageSize);

/**
* 点赞
*
* @param userId
* @param publishId
* @return
*/
boolean saveLikeComment(Long userId, String publishId);

/**
* 取消点赞、喜欢等
*
* @param userId
* @param publishId
* @return
*/
boolean removeComment(Long userId, String publishId, Integer commentType);

/**
* 喜欢
*
* @param userId
* @param publishId
* @return
*/
boolean saveLoveComment(Long userId, String publishId);

/**
* 保存评论
*
* @param userId
* @param publishId
* @param type
* @param content
* @return
*/
boolean saveComment(Long userId, String publishId, Integer type, String content);

/**
* 查询评论数
*
* @param publishId
* @param type
* @return
*/
Long queryCommentCount(String publishId, Integer type);


/**
* 根据id查询
*
* @param publishId
* @return
*/
Publish queryPublishById(String publishId);

/**
* 查询评论
*
* @return
*/
PageInfo<Comment> queryCommentList(String publishId, Integer page, Integer pageSize);

}

编写实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
package com.tanhua.dubbo.server.api;

import com.alibaba.dubbo.config.annotation.Service;
import com.mongodb.client.result.DeleteResult;
import com.tanhua.dubbo.server.pojo.*;
import com.tanhua.dubbo.server.vo.PageInfo;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
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.ArrayList;
import java.util.List;

@Service(version = "1.0.0")
public class QuanziApiImpl implements QuanZiApi {

@Autowired
private MongoTemplate mongoTemplate;



@Override
public boolean saveLikeComment(Long userId, String publishId) {

//判断是否已经点赞,如果已经点赞就返回
Criteria criteria = Criteria.where("userId").is(userId)
.and("publishId").is(new ObjectId(publishId))
.and("commentType").is(1);
Query query = Query.query(criteria);
long count = this.mongoTemplate.count(query, Comment.class);
if (count > 0) {
return false;
}

return this.saveComment(userId, publishId, 1, null);
}

@Override
public boolean removeComment(Long userId, String publishId, Integer commentType) {

Criteria criteria = Criteria.where("userId").is(userId)
.and("publishId").is(new ObjectId(publishId))
.and("commentType").is(commentType);
Query query = Query.query(criteria);

DeleteResult deleteResult = this.mongoTemplate.remove(query, Comment.class);
return deleteResult.getDeletedCount() > 0;
}

@Override
public boolean saveLoveComment(Long userId, String publishId) {
//判断是否已经喜欢,如果已经喜欢就返回
Criteria criteria = Criteria.where("userId").is(userId)
.and("publishId").is(new ObjectId(publishId))
.and("commentType").is(3);
Query query = Query.query(criteria);
long count = this.mongoTemplate.count(query, Comment.class);
if (count > 0) {
return false;
}

return this.saveComment(userId, publishId, 3, null);
}

@Override
public boolean saveComment(Long userId, String publishId, Integer type, String content) {

try {
Comment comment = new Comment();
comment.setContent(content);
comment.setIsParent(true);
comment.setCommentType(type);
comment.setPublishId(new ObjectId(publishId));
comment.setUserId(userId);
comment.setId(ObjectId.get());
comment.setCreated(System.currentTimeMillis());

this.mongoTemplate.save(comment);

return true;
} catch (Exception e) {
e.printStackTrace();
}

return false;
}

@Override
public Long queryCommentCount(String publishId, Integer type) {
Criteria criteria = Criteria.where("publishId").is(new ObjectId(publishId))
.and("commentType").is(type);
Query query = Query.query(criteria);
return this.mongoTemplate.count(query, Comment.class);
}


}

编写接口服务

MovementsController:

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

/**
* 取消点赞
*
* @param publishId
* @return
*/
@GetMapping("/{id}/dislike")
public ResponseEntity<Long> disLikeComment(@PathVariable("id") String publishId) {
try {
Long count = this.movementsService.disLikeComment(publishId);
if (null != count) {
return ResponseEntity.ok(count);
}
} catch (Exception e) {
e.printStackTrace();
}
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}

编写服务实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package com.tanhua.server.service;

import com.alibaba.dubbo.common.utils.CollectionUtils;
import com.alibaba.dubbo.common.utils.StringUtils;
import com.alibaba.dubbo.config.annotation.Reference;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.tanhua.dubbo.server.api.QuanZiApi;
import com.tanhua.dubbo.server.pojo.Publish;
import com.tanhua.dubbo.server.vo.PageInfo;
import com.tanhua.server.pojo.User;
import com.tanhua.server.pojo.UserInfo;
import com.tanhua.server.utils.RelativeDateFormat;
import com.tanhua.server.utils.UserThreadLocal;
import com.tanhua.server.vo.Movements;
import com.tanhua.server.vo.PageResult;
import com.tanhua.server.vo.PicUploadResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.util.*;

@Service
public class MovementsService {

@Reference(version = "1.0.0")
private QuanZiApi quanZiApi;

@Autowired
private PicUploadService picUploadService;

@Autowired
private UserInfoService userInfoService;

@Autowired
private RedisTemplate<String, String> redisTemplate;


public Long likeComment(String publishId) {
User user = UserThreadLocal.get();

boolean bool = this.quanZiApi.saveLikeComment(user.getId(), publishId);
if (!bool) {
//保存失败
return null;
}

// 保存成功,获取点赞数
Long likeCount = 0L;
String likeCommentKey = "QUANZI_COMMENT_LIKE_" + publishId;
if (!this.redisTemplate.hasKey(likeCommentKey)) {
Long count = this.quanZiApi.queryCommentCount(publishId, 1);
likeCount = count;
this.redisTemplate.opsForValue().set(likeCommentKey, String.valueOf(likeCount));
} else {
likeCount = this.redisTemplate.opsForValue().increment(likeCommentKey);
}

// 记录当前用于已经点赞
String likeUserCommentKey = "QUANZI_COMMENT_LIKE_USER_" + user.getId() + "_" + publishId;
this.redisTemplate.opsForValue().set(likeUserCommentKey, "1");

return likeCount;
}

public Long disLikeComment(String publishId) {

User user = UserThreadLocal.get();
boolean bool = this.quanZiApi.removeComment(user.getId(), publishId, 1);
if (!bool) {
return null;
}

// redis中的点赞数需要减少1
String likeCommentKey = "QUANZI_COMMENT_LIKE_" + publishId;
Long count = this.redisTemplate.opsForValue().decrement(likeCommentKey);

// 删除该用户的标记点赞
String likeUserCommentKey = "QUANZI_COMMENT_LIKE_USER_" + user.getId() + "_" + publishId;
this.redisTemplate.delete(likeUserCommentKey);

return count;
}


}

修改查询动态点赞数

测试

喜欢

MovementsController

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

/**
* 取消喜欢
*
* @param publishId
* @return
*/
@GetMapping("/{id}/unlove")
public ResponseEntity<Long> unLoveComment(@PathVariable("id") String publishId) {
try {
Long count = this.movementsService.unLoveComment(publishId);
if (null != count) {
return ResponseEntity.ok(count);
}
} catch (Exception e) {
e.printStackTrace();
}
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}

MovementsService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public Long loveComment(String publishId) {
User user = UserThreadLocal.get();

boolean bool = this.quanZiApi.saveLoveComment(user.getId(), publishId);
if (!bool) {
//保存失败
return null;
}

// 保存成功,获取喜欢数
Long loveCount = 0L;
String likeCommentKey = "QUANZI_COMMENT_LOVE_" + publishId;
if (!this.redisTemplate.hasKey(likeCommentKey)) {
Long count = this.quanZiApi.queryCommentCount(publishId, 1);
loveCount = count;
this.redisTemplate.opsForValue().set(likeCommentKey, String.valueOf(loveCount));
} else {
loveCount = this.redisTemplate.opsForValue().increment(likeCommentKey);
}

// 记录当前用于已经喜欢
String likeUserCommentKey = "QUANZI_COMMENT_LOVE_USER_" + user.getId() + "_" + publishId;
this.redisTemplate.opsForValue().set(likeUserCommentKey, "1");

return loveCount;
}

public Long unLoveComment(String publishId) {
User user = UserThreadLocal.get();
boolean bool = this.quanZiApi.removeComment(user.getId(), publishId, 3);
if (!bool) {
return null;
}

// redis中的喜欢数需要减少1
String likeCommentKey = "QUANZI_COMMENT_LOVE_" + publishId;
Long count = this.redisTemplate.opsForValue().decrement(likeCommentKey);

// 删除该用户的标记喜欢
String likeUserCommentKey = "QUANZI_COMMENT_LOVE_USER_" + user.getId() + "_" + publishId;
this.redisTemplate.delete(likeUserCommentKey);

return count;
}

测试


查询单条动态

定义dubbo接口

QuanZiApi:

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

dubbo接口实现

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

定义服务接口

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

服务实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

private List<Movements> fillValueToMovements(List<Publish> records){
User user = UserThreadLocal.get();
List<Movements> movementsList = new ArrayList<>();
List<Long> userIds = new ArrayList<>();
for (Publish record : records) {
Movements movements = new Movements();

movements.setId(record.getId().toHexString());
movements.setUserId(record.getUserId());

if (!userIds.contains(record.getUserId())) {
userIds.add(record.getUserId());
}

String likeUserCommentKey = "QUANZI_COMMENT_LIKE_USER_" + user.getId() + "_" + movements.getId();
movements.setHasLiked(this.redisTemplate.hasKey(likeUserCommentKey) ? 1 : 0); //是否点赞

String likeCommentKey = "QUANZI_COMMENT_LIKE_" + movements.getId();
String value = this.redisTemplate.opsForValue().get(likeCommentKey);
if(StringUtils.isNotEmpty(value)){
movements.setLikeCount(Integer.valueOf(value)); //点赞数
}else{
movements.setLikeCount(0); //点赞数
}

String loveUserCommentKey = "QUANZI_COMMENT_LOVE_USER_" + user.getId() + "_" + movements.getId();
movements.setHasLoved(this.redisTemplate.hasKey(loveUserCommentKey) ? 1 : 0); //是否喜欢

String loveCommentKey = "QUANZI_COMMENT_LOVE_" + movements.getId();
String loveValue = this.redisTemplate.opsForValue().get(loveCommentKey);
if(StringUtils.isNotEmpty(loveValue)){
movements.setLoveCount(Integer.valueOf(loveValue)); //喜欢数
}else{
movements.setLoveCount(0); //喜欢数
}

movements.setDistance("1.2公里"); //TODO 距离
movements.setCommentCount(30); //TODO 评论数
movements.setCreateDate(RelativeDateFormat.format(new Date(record.getCreated()))); //发布时间,10分钟前
movements.setTextContent(record.getText());
movements.setImageContent(record.getMedias().toArray(new String[]{}));

movementsList.add(movements);
}

public Movements queryMovementsById(String publishId) {
Publish publish = this.quanZiApi. (publishId);
if(null == publish){
return null;
}

//查询到动态数据,数据的填充
List<Movements> movementsList = this.fillValueToMovements(Arrays.asList(publish));
return movementsList.get(0);
}

测试


单条动态评论

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

定义dubbo接口

1
2
3
4
5
6
/**
* 查询评论
*
* @return
*/
PageInfo<Comment> queryCommentList(String publishId, Integer page, Integer pageSize);

编写实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Override
public PageInfo<Comment> queryCommentList(String publishId, Integer page, Integer pageSize) {

PageRequest pageRequest = PageRequest.of(page - 1, pageSize, Sort.by(Sort.Order.asc("created")));

Query query = Query.query(Criteria.where("publishId").is(new ObjectId(publishId))
.and("commentType").is(2)).with(pageRequest);

List<Comment> commentList = this.mongoTemplate.find(query, Comment.class);

PageInfo<Comment> pageInfo = new PageInfo<>();
pageInfo.setTotal(0);
pageInfo.setPageSize(pageSize);
pageInfo.setPageNum(page);
pageInfo.setRecords(commentList);

return pageInfo;
}

服务接口实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.tanhua.server.vo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
* 评论
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Comments {

private String id; //评论id
private String avatar; //头像
private String nickname; //昵称
private String content; //评论
private String createDate; //评论时间: 08:27
private Integer likeCount; //点赞数
private Integer hasLiked; //是否点赞(1是,0否)

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package com.tanhua.server.controller;

import com.tanhua.server.service.CommentsService;
import com.tanhua.server.service.MovementsService;
import com.tanhua.server.vo.PageResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

@RestController
@RequestMapping("comments")
public class CommentsController {

@Autowired
private CommentsService commentsService;

@Autowired
private MovementsService movementsService;

/**
* 查询评论列表
*
* @param publishId
* @param page
* @param pageSize
* @return
*/
@GetMapping
public ResponseEntity<PageResult> queryCommentsList(@RequestParam("movementId") String publishId,
@RequestParam(value = "page", defaultValue = "1") Integer page,
@RequestParam(value = "pagesize", defaultValue = "10") Integer pageSize) {
try {
PageResult pageResult = this.commentsService.queryCommentsList(publishId, page, pageSize);
if (null != pageResult) {
return ResponseEntity.ok(pageResult);
}
} catch (Exception e) {
e.printStackTrace();
}
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}


/**
* 发表评论
*
* @param param
* @return
*/
@PostMapping
public ResponseEntity<Void> saveComments(@RequestBody Map<String, String> param) {
try {
String publishId = param.get("movementId");
String content = param.get("comment");
Boolean bool = this.commentsService.saveComments(publishId, content);
if (bool) {
return ResponseEntity.ok(null);
}
} catch (Exception e) {
e.printStackTrace();
}
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}

/**
* 点赞
*
* @param publishId
* @return
*/
@GetMapping("/{id}/like")
public ResponseEntity<Long> likeComment(@PathVariable("id") String publishId) {
try {
Long count = this.movementsService.likeComment(publishId);
if (null != count) {
return ResponseEntity.ok(count);
}
} catch (Exception e) {
e.printStackTrace();
}
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}

/**
* 取消点赞
*
* @param publishId
* @return
*/
@GetMapping("/{id}/dislike")
public ResponseEntity<Long> disLikeComment(@PathVariable("id") String publishId) {
try {
Long count = this.movementsService.disLikeComment(publishId);
if (null != count) {
return ResponseEntity.ok(count);
}
} catch (Exception e) {
e.printStackTrace();
}
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
package com.tanhua.server.service;

import com.alibaba.dubbo.common.utils.CollectionUtils;
import com.alibaba.dubbo.common.utils.StringUtils;
import com.alibaba.dubbo.config.annotation.Reference;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.tanhua.dubbo.server.api.QuanZiApi;
import com.tanhua.dubbo.server.pojo.Comment;
import com.tanhua.dubbo.server.vo.PageInfo;
import com.tanhua.server.pojo.User;
import com.tanhua.server.pojo.UserInfo;
import com.tanhua.server.utils.UserThreadLocal;
import com.tanhua.server.vo.Comments;
import com.tanhua.server.vo.PageResult;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
public class CommentsService {

@Reference(version = "1.0.0")
private QuanZiApi quanZiApi;

@Autowired
private UserInfoService userInfoService;

@Autowired
private RedisTemplate<String, String> redisTemplate;

public PageResult queryCommentsList(String publishId, Integer page, Integer pageSize) {

User user = UserThreadLocal.get();

PageResult pageResult = new PageResult();
pageResult.setPage(page);
pageResult.setPagesize(pageSize);
pageResult.setCounts(0);
pageResult.setPages(0);

PageInfo<Comment> pageInfo = this.quanZiApi.queryCommentList(publishId, page, pageSize);
List<Comment> records = pageInfo.getRecords();

if (CollectionUtils.isEmpty(records)) {
return pageResult;
}

List<Long> userIds = new ArrayList<>();
for (Comment record : records) {
if(!userIds.contains(record.getUserId())){
userIds.add(record.getUserId());
}
}

QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>();
queryWrapper.in("user_id", userIds);
List<UserInfo> userInfoList = this.userInfoService.queryUserInfoList(queryWrapper);

List<Comments> commentsList = new ArrayList<>();
for (Comment record : records) {
Comments comments = new Comments();

comments.setId(record.getId().toHexString());
comments.setCreateDate(new DateTime(record.getCreated()).toString("yyyy年MM月dd日 HH:mm"));
comments.setContent(record.getContent());

for (UserInfo userInfo : userInfoList) {
if(record.getUserId().longValue() == userInfo.getUserId().longValue()){
comments.setNickname(userInfo.getNickName());
comments.setAvatar(userInfo.getLogo());
break;
}
}

String likeUserCommentKey = "QUANZI_COMMENT_LIKE_USER_" + user.getId() + "_" + comments.getId();
comments.setHasLiked(this.redisTemplate.hasKey(likeUserCommentKey) ? 1 : 0); //是否点赞

String likeCommentKey = "QUANZI_COMMENT_LIKE_" + comments.getId();
String value = this.redisTemplate.opsForValue().get(likeCommentKey);
if(StringUtils.isNotEmpty(value)){
comments.setLikeCount(Integer.valueOf(value)); //点赞数
}else{
comments.setLikeCount(0); //点赞数
}

commentsList.add(comments);
}

pageResult.setItems(commentsList);

return pageResult;
}

public Boolean saveComments(String publishId, String content) {
User user = UserThreadLocal.get();
return this.quanZiApi.saveComment(user.getId(), publishId, 2, content);
}
}

测试


小视频功能说明

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

效果:

查看详情:

评论:

点赞:

技术方案

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

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

FastDFS环境搭建

搭建服务

我们使用docker进行搭建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#拉趣镜像
docker pull delron/fastdfs

#创建tracker容器
docker run -d --network=host --name tracker -v /var/fdfs/tracker:/var/fdfs delron/fastdfs tracker

#创建storage容器
docker run -d --network=host --name storage -e TRACKER_SERVER=101.33.203.115:22122 -v /var/fdfs/storage:/var/fdfs -e GROUP_NAME=group1 delron/fastdfs storage

#进入storage容器,到storage的配置文件中配置http访问的端口,配置文件在/etc/fdfs目录下的storage.conf。
docker exec -it storage /bin/bash

#默认的http端口为8888,可以修改也可以配置
# the port of the web server on this storage server
http.server_port=8888

#配置nginx,在cd /usr/local/nginx/conf目录下,修改nginx.conf文件
#默认配置如下:
server {
listen 8888;
server_name localhost;
location ~/group[0-9]/ {
ngx_fastdfs_module;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}

#默认的存储路径为/var/fdfs/data

修改了端口,切记要重启容器docker restart ***

java client

导入依赖:

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

application.properties

1
2
3
4
5
6
7
8
9
10
11
# 分布式文件系统FDFS配置

fdfs.so-timeout = 1501
fdfs.connect-timeout = 601

#缩略图生成参数
fdfs.thumb-image.width= 150
fdfs.thumb-image.height= 150

#TrackerList参数,支持多个
fdfs.tracker-list=192.168.31.81:22122

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.tanhua.server;

import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import org.apache.commons.io.FileUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.io.File;
import java.io.IOException;

@RunWith(SpringRunner.class)
@SpringBootTest
public class TestFastDFS {

@Autowired
protected FastFileStorageClient storageClient;

@Test
public void testUpload(){
String path = "C:\\Users\\zhijun\\Desktop\\pics\\1.jpg";
File file = new File(path);

try {
StorePath storePath = this.storageClient.uploadFile(FileUtils.openInputStream(file), file.length(), "jpg", null);

System.out.println(storePath); //StorePath [group=group1, path=M00/00/00/wKgfUV2GJSuAOUd_AAHnjh7KpOc1.1.jpg]
System.out.println(storePath.getFullPath());//group1/M00/00/00/wKgfUV2GJSuAOUd_AAHnjh7KpOc1.1.jpg
} catch (IOException e) {
e.printStackTrace();
}
}
}

发布小视频

编写pojo

在dubbo接口工程中编写pojo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.tanhua.dubbo.server.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.bson.types.ObjectId;
import org.springframework.data.mongodb.core.mapping.Document;

import java.util.List;

@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 userId;
private String text; //文字
private String picUrl; //视频封面文件
private String videoUrl; //视频文件
private Long created; //创建时间
private Integer seeType; // 谁可以看,1-公开,2-私密,3-部分可见,4-不给谁看
private List<Long> seeList; //部分可见的列表
private List<Long> notSeeList; //不给谁看的列表
private String longitude; //经度
private String latitude; //纬度
private String locationName; //位置名称
}

定义接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.tanhua.dubbo.server.api;

import com.tanhua.dubbo.server.pojo.Video;
import com.tanhua.dubbo.server.vo.PageInfo;

public interface VideoApi {

/**
* 保存小视频
*
* @param video
* @return
*/
Boolean saveVideo(Video video);

/**
* 分页查询小视频列表,按照时间倒序排序
*
* @param page
* @param pageSize
* @return
*/
PageInfo<Video> queryVideoList(Integer page, Integer pageSize);

/**
* 关注用户
*
* @param userId
* @param followUserId
* @return
*/
Boolean followUser(Long userId, Long followUserId);

/**
* 取消关注用户
*
* @param userId
* @param followUserId
* @return
*/
Boolean disFollowUser(Long userId, Long followUserId);

}

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package com.tanhua.dubbo.server.api;

import com.alibaba.dubbo.config.annotation.Service;
import com.mongodb.client.result.DeleteResult;
import com.tanhua.dubbo.server.pojo.FollowUser;
import com.tanhua.dubbo.server.pojo.Video;
import com.tanhua.dubbo.server.vo.PageInfo;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
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;

@Service(version = "1.0.0")
public class VideoApiImpl implements VideoApi {

@Autowired
private MongoTemplate mongoTemplate;

@Override
public Boolean saveVideo(Video video) {

if (video.getUserId() == null) {
return false;
}

video.setId(ObjectId.get());
video.setCreated(System.currentTimeMillis());
this.mongoTemplate.save(video);

return true;
}

@Override
public PageInfo<Video> queryVideoList(Integer page, Integer pageSize) {
Pageable pageable = PageRequest.of(page - 1, pageSize, Sort.by(Sort.Order.desc("created")));
Query query = new Query().with(pageable);
List<Video> videos = this.mongoTemplate.find(query, Video.class);
PageInfo<Video> pageInfo = new PageInfo<>();
pageInfo.setRecords(videos);
pageInfo.setPageNum(page);
pageInfo.setPageSize(pageSize);
pageInfo.setTotal(0); //不提供总数
return pageInfo;
}

@Override
public Boolean followUser(Long userId, Long followUserId) {
try {
FollowUser followUser = new FollowUser();
followUser.setId(ObjectId.get());
followUser.setUserId(userId);
followUser.setFollowUserId(followUserId);
followUser.setCreated(System.currentTimeMillis());
this.mongoTemplate.save(followUser);
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
}

接口服务

VideoController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package com.tanhua.server.controller;

import com.tanhua.server.service.VideoService;
import com.tanhua.server.vo.PageResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.Map;

@RestController
@RequestMapping("smallVideos")
public class VideoController {

@Autowired
private VideoService videoService;

@Autowired
private MovementsController movementsController;

@Autowired
private CommentsController commentsController;

@PostMapping
public ResponseEntity<Void> saveVideo(@RequestParam(value = "videoThumbnail", required = false) MultipartFile picFile,
@RequestParam(value = "videoFile", required = false) MultipartFile videoFile) {
try {
Boolean bool = this.videoService.saveVideo(picFile, videoFile);
if (bool) {
return ResponseEntity.ok(null);
}
} catch (Exception e) {
e.printStackTrace();
}
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}

/**
* 查询小视频列表
*
* @param page
* @param pageSize
* @return
*/
@GetMapping
public ResponseEntity<PageResult> queryVideoList(@RequestParam(value = "page", defaultValue = "1") Integer page,
@RequestParam(value = "pagesize", defaultValue = "10") Integer pageSize) {
try {
if (page <= 0) {
page = 1;
}
PageResult pageResult = this.videoService.queryVideoList(page, pageSize);
if (null != pageResult) {
return ResponseEntity.ok(pageResult);
}
} catch (Exception e) {
e.printStackTrace();
}
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}



}

VideoService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package com.tanhua.server.service;

import com.alibaba.dubbo.config.annotation.Reference;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
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.dubbo.server.api.QuanZiApi;
import com.tanhua.dubbo.server.api.VideoApi;
import com.tanhua.dubbo.server.pojo.Video;
import com.tanhua.dubbo.server.vo.PageInfo;
import com.tanhua.server.pojo.User;
import com.tanhua.server.pojo.UserInfo;
import com.tanhua.server.utils.UserThreadLocal;
import com.tanhua.server.vo.PageResult;
import com.tanhua.server.vo.PicUploadResult;
import com.tanhua.server.vo.VideoVo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.util.ArrayList;
import java.util.List;

@Service
public class VideoService {

@Reference(version = "1.0.0")
private VideoApi videoApi;

@Autowired
private PicUploadService picUploadService;

@Autowired
protected FastFileStorageClient storageClient;

@Autowired
private FdfsWebServer fdfsWebServer;

public Boolean saveVideo(MultipartFile picFile, MultipartFile videoFile) {

User user = UserThreadLocal.get();

Video video = new Video();
video.setUserId(user.getId());
video.setSeeType(1);

try {
// 上传图片
PicUploadResult picUploadResult = this.picUploadService.upload(picFile);
video.setPicUrl(picUploadResult.getName());

//上传视频
StorePath storePath = storageClient.uploadFile(videoFile.getInputStream(),
videoFile.getSize(),
StringUtils.substringAfter(videoFile.getOriginalFilename(), "."),
null);
video.setVideoUrl(fdfsWebServer.getWebServerUrl() + storePath.getFullPath());

return this.videoApi.saveVideo(video);
} catch (Exception e) {
e.printStackTrace();
}

return false;
}


}

测试

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

解决:application.properties

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

问题解决:

小视频列表

定义dubbo服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.tanhua.dubbo.server.api;

import com.tanhua.dubbo.server.pojo.Video;
import com.tanhua.dubbo.server.vo.PageInfo;

public interface VideoApi {

/**
* 保存小视频
*
* @param video
* @return
*/
Boolean saveVideo(Video video);

/**
* 分页查询小视频列表,按照时间倒序排序
*
* @param page
* @param pageSize
* @return
*/
PageInfo<Video> queryVideoList(Integer page, Integer pageSize);

}

实现dubbo服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package com.tanhua.dubbo.server.api;

import com.alibaba.dubbo.config.annotation.Service;
import com.mongodb.client.result.DeleteResult;
import com.tanhua.dubbo.server.pojo.FollowUser;
import com.tanhua.dubbo.server.pojo.Video;
import com.tanhua.dubbo.server.vo.PageInfo;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
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;

@Service(version = "1.0.0")
public class VideoApiImpl implements VideoApi {

@Autowired
private MongoTemplate mongoTemplate;

@Override
public Boolean saveVideo(Video video) {

if (video.getUserId() == null) {
return false;
}

video.setId(ObjectId.get());
video.setCreated(System.currentTimeMillis());
this.mongoTemplate.save(video);

return true;
}

@Override
public PageInfo<Video> queryVideoList(Integer page, Integer pageSize) {
Pageable pageable = PageRequest.of(page - 1, pageSize, Sort.by(Sort.Order.desc("created")));
Query query = new Query().with(pageable);
List<Video> videos = this.mongoTemplate.find(query, Video.class);
PageInfo<Video> pageInfo = new PageInfo<>();
pageInfo.setRecords(videos);
pageInfo.setPageNum(page);
pageInfo.setPageSize(pageSize);
pageInfo.setTotal(0); //不提供总数
return pageInfo;
}
}

定义VideoVo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.tanhua.server.vo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class VideoVo {

private String id;
private Long userId;
private String avatar; //头像
private String nickname; //昵称
private String cover; //封面
private String videoUrl; //视频URL
private String signature; //签名
private Integer likeCount; //点赞数量
private Integer hasLiked; //是否已赞(1是,0否)
private Integer hasFocus; //是是否关注 (1是,0否)
private Integer commentCount; //评论数量
}

VideoController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package com.tanhua.server.controller;

import com.tanhua.server.service.VideoService;
import com.tanhua.server.vo.PageResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.Map;

@RestController
@RequestMapping("smallVideos")
public class VideoController {

@Autowired
private VideoService videoService;

@Autowired
private MovementsController movementsController;

@Autowired
private CommentsController commentsController;

/**
* 查询小视频列表
*
* @param page
* @param pageSize
* @return
*/
@GetMapping
public ResponseEntity<PageResult> queryVideoList(@RequestParam(value = "page", defaultValue = "1") Integer page,
@RequestParam(value = "pagesize", defaultValue = "10") Integer pageSize) {
try {
if (page <= 0) {
page = 1;
}
PageResult pageResult = this.videoService.queryVideoList(page, pageSize);
if (null != pageResult) {
return ResponseEntity.ok(pageResult);
}
} catch (Exception e) {
e.printStackTrace();
}
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}

VideoService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
public PageResult queryVideoList(Integer page, Integer pageSize) {
User user = UserThreadLocal.get();

PageResult pageResult = new PageResult();
pageResult.setPage(page);
pageResult.setPagesize(pageSize);
pageResult.setPages(0);
pageResult.setCounts(0);

PageInfo<Video> pageInfo = this.videoApi.queryVideoList(page, pageSize);
List<Video> records = pageInfo.getRecords();
List<VideoVo> videoVoList = new ArrayList<>();
List<Long> userIds = new ArrayList<>();

for (Video record : records) {
VideoVo videoVo = new VideoVo();

videoVo.setUserId(record.getUserId());
videoVo.setCover(record.getPicUrl());
videoVo.setVideoUrl(record.getVideoUrl());
videoVo.setId(record.getId().toHexString());
videoVo.setSignature("我就是我~"); //TODO 签名

Long commentCount = this.quanZiApi.queryCommentCount(videoVo.getId(), 2);
videoVo.setCommentCount(commentCount == null ? 0 : commentCount.intValue()); // 评论数

videoVo.setHasFocus(0); //TODO 是否关注

String likeUserCommentKey = "QUANZI_COMMENT_LIKE_USER_" + user.getId() + "_" + videoVo.getId();
videoVo.setHasLiked(this.redisTemplate.hasKey(likeUserCommentKey) ? 1 : 0); //是否点赞

String likeCommentKey = "QUANZI_COMMENT_LIKE_" + videoVo.getId();
String value = this.redisTemplate.opsForValue().get(likeCommentKey);
if(StringUtils.isNotEmpty(value)){
videoVo.setLikeCount(Integer.valueOf(value)); //点赞数
}else{
videoVo.setLikeCount(0); //点赞数
}

if (!userIds.contains(record.getUserId())) {
userIds.add(record.getUserId());
}

videoVoList.add(videoVo);
}


QueryWrapper<UserInfo> queryWrapper = new QueryWrapper();
queryWrapper.in("user_id", userIds);
List<UserInfo> userInfos = this.userInfoService.queryUserInfoList(queryWrapper);
for (VideoVo videoVo : videoVoList) {
for (UserInfo userInfo : userInfos) {
if (videoVo.getUserId().longValue() == userInfo.getUserId().longValue()) {

videoVo.setNickname(userInfo.getNickName());
videoVo.setAvatar(userInfo.getLogo());

break;
}
}

}

pageResult.setItems(videoVoList);

return pageResult;
}

测试


视频点赞

点赞逻辑与之前的圈子点赞一样,所以实现也是一样的。
mock地址:https://mock.boxuegu.com/project/164/interface/api/77987

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

/**
* 取消点赞
*
* @param videoId
* @return
*/
@PostMapping("/{id}/dislike")
public ResponseEntity<Long> disLikeComment(@PathVariable("id") String videoId) {
return this.movementsController.disLikeComment(videoId);
}

小视频评论

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

评论列表

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

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

发布评论

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

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

评论点赞

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

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

/**
* 评论取消点赞
*
* @param publishId
* @return
*/
@PostMapping("/comments/{id}/dislike")
public ResponseEntity<Long> disCommentsLikeComment(@PathVariable("id") String publishId) {
return this.movementsController.disLikeComment(publishId);
}

关注用户

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

定义dubbo服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.tanhua.dubbo.server.pojo;

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 = "follow_user")
public class FollowUser implements java.io.Serializable{

private static final long serialVersionUID = 3148619072405056052L;

private ObjectId id; //主键id
private Long userId; //用户id
private Long followUserId; //关注的用户id
private Long created; //关注时间
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.tanhua.dubbo.server.api;

import com.tanhua.dubbo.server.pojo.Video;
import com.tanhua.dubbo.server.vo.PageInfo;

public interface VideoApi {

/**
* 保存小视频
*
* @param video
* @return
*/
Boolean saveVideo(Video video);

/**
* 分页查询小视频列表,按照时间倒序排序
*
* @param page
* @param pageSize
* @return
*/
PageInfo<Video> queryVideoList(Integer page, Integer pageSize);

/**
* 关注用户
*
* @param userId
* @param followUserId
* @return
*/
Boolean followUser(Long userId, Long followUserId);

/**
* 取消关注用户
*
* @param userId
* @param followUserId
* @return
*/
Boolean disFollowUser(Long userId, Long followUserId);

}

接口实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package com.tanhua.dubbo.server.api;

import com.alibaba.dubbo.config.annotation.Service;
import com.mongodb.client.result.DeleteResult;
import com.tanhua.dubbo.server.pojo.FollowUser;
import com.tanhua.dubbo.server.pojo.Video;
import com.tanhua.dubbo.server.vo.PageInfo;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
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;

@Service(version = "1.0.0")
public class VideoApiImpl implements VideoApi {

@Autowired
private MongoTemplate mongoTemplate;

@Override
public Boolean followUser(Long userId, Long followUserId) {
try {
FollowUser followUser = new FollowUser();
followUser.setId(ObjectId.get());
followUser.setUserId(userId);
followUser.setFollowUserId(followUserId);
followUser.setCreated(System.currentTimeMillis());
this.mongoTemplate.save(followUser);
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}

@Override
public Boolean disFollowUser(Long userId, Long followUserId) {
Query query = Query.query(Criteria.where("userId").is(userId).and("followUserId").is(followUserId));
DeleteResult deleteResult = this.mongoTemplate.remove(query, FollowUser.class);
return deleteResult.getDeletedCount() > 0;
}
}

服务实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//VideoController:

/**
* 视频用户关注
*/
@PostMapping("/{id}/userFocus")
public ResponseEntity<Void> saveUserFocusComments(@PathVariable("id") Long userId) {
try {
Boolean bool = this.videoService.followUser(userId);
if (bool) {
return ResponseEntity.ok(null);
}
} catch (Exception e) {
e.printStackTrace();
}
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}

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

//记录已关注
String followUserKey = "VIDEO_FOLLOW_USER_" + user.getId() + "_" + userId;
this.redisTemplate.opsForValue().set(followUserKey, "1");

return true;
}

/**
* 取消关注
*
* @param userId
* @return
*/
public Boolean disFollowUser(Long userId) {
User user = UserThreadLocal.get();
this.videoApi.disFollowUser(user.getId(), userId);

String followUserKey = "VIDEO_FOLLOW_USER_" + user.getId() + "_" + userId;
this.redisTemplate.delete(followUserKey);

return true;
}