18.查询好友动态和推荐动态
查询好友动态和推荐动态
一、查询好友动态
代码步骤
- Controller层接受请求参数
- Service数据封装
- 调用API查询好友动态详情数据
- 调用API查询动态发布人详情
- 构造VO对象
- API层根据用户ID查询好友发布动态详情
- 查询好友时间线表
- 查询动态详情
1.1Controller
/**
* 查看好友动态
* 1.请求路径::/movements
* 2.请求方式:get
* 3.请求参数:page,当前页码;pagesize,每页展示数
* 4.响应数据:PageResult
*/
@GetMapping
public ResponseEntity viewSocialUpdates(@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer pageSize){
//1.接收参数,调用业务层完成查询
PageResult pageResult = movementsService.viewSocialUpdates(page,pageSize);
//2.构造返回值
return ResponseEntity.ok(pageResult);
}
1.2、Service
/**
* 查看好友动态
*/
public PageResult viewSocialUpdates(Integer page, Integer pageSize) {
//1.获取当前登录的用户id
Long userId = ThreadLocalUtils.getUserId();
//2.调用api查看好友的动态详情集合
List<Movement> movements = movementsApi.viewSocialUpdates(userId, page, pageSize);
//3.判断movements是否为空,为空直接new一个PageResult返回,为空的意思是当前登录的用户没有好友发布动态
if(CollUtil.isEmpty(movements)){
//ddd,这个害我搞了半天,之前我用的是movements == null,害死人//走到 这步movements为空的时候程序会一直往下走,不会停掉返回;然后就会在下面掉用userInfo的时候因为movements为空报mysql的sql语法错误
return new PageResult();
}
//4.先批量获取好友动态详情表中的用户id,再根据批量的用户id批量获取用户详情
List<Long> userIds = CollUtil.getFieldValues(movements, "userId", Long.class);
Map<Long, UserInfo> userInfoMap = userInfoApi.batchQueryUserInfo(userIds, null);//null参数位置为条件
//5.遍历movements集合,每遍历一个动态详情表就构造一个vo对象
List<MovementsVo> movementsVos = new ArrayList<>();
for (Movement movement : movements) {
Long id = movement.getUserId();
UserInfo userInfo = userInfoMap.get(id);
if(userInfo != null){
MovementsVo movementsVo = MovementsVo.init(userInfo, movement);
//修复动态点赞bug
String key = Constants.MOVEMENTS_INTERACT_KEY + movement.getId().toHexString();//动态id
String hashKey = Constants.MOVEMENT_LIKE_HASHKEY + ThreadLocalUtils.getUserId();
if(redisTemplate.opsForHash().hasKey(key, hashKey)){
movementsVo.setHasLiked(1);
}
String loveHashKey = Constants.MOVEMENT_LOVE_HASHKEY + ThreadLocalUtils.getUserId();
if(redisTemplate.opsForHash().hasKey(key, loveHashKey)){
movementsVo.setHasLoved(1);
}
movementsVos.add(movementsVo);
}
}
//6.构造返回条件
return new PageResult(page,pageSize,0,movementsVos);
}
1.3、Api层
/**
*查看好友动态
*/
public List<Movement> viewSocialUpdates(Long friendId, Integer page, Integer pageSize) {
//1.根据用户id查询时间线表
//1.1创建Criteria对象,构造查询条件
Criteria criteria = Criteria.where("friendId").is(friendId);
//1.2根据Criteria创建Query对象
Query query = Query.query(criteria)
.skip((page - 1) * pageSize)
.limit(pageSize).with(Sort.by(Sort.Order.desc("created")));
List<MovementTimeLine> timeLines = mongoTemplate.find(query, MovementTimeLine.class);
//2.在时间线表批量获取动态详情的id
//这个list集合装的是好友的动态详情id
List<ObjectId> movementIds = CollUtil.getFieldValues(timeLines, "movementId", ObjectId.class);
//3.根据动态详情id查询动态
Criteria criteria1 = Criteria.where("id").in(movementIds);
Query query1 = Query.query(criteria1);
List<Movement> movements = mongoTemplate.find(query1, Movement.class);
return movements;
}
二、推荐动态
推荐动态是通过推荐系统计算出的结果,现在我们只需要实现查询即可,推荐系统在后面的课程中完成。
推荐系统计算完成后,会将结果数据写入到Redis中,数据如下:
key: MOVEMENTS_RECOMMEND_1
value: "2562,3639,2063,3448,2128,2597,2893,2333,3330,2642,2541,3002,3561,3649,2384,2504,3397,2843,2341,2249"
可以看到,在Redis中的数据是有多个发布id组成(pid)由逗号分隔。所以实现中需要自己对这些数据做分页处理。
代码步骤
- Controller层接受请求参数
- Service数据封装
- 从redis获取当前用户的推荐PID列表
- 如果不存在,调用API随机获取10条动态数据
- 如果存在,调用API根据PID列表查询动态数据
- 构造VO对象
- API层编写方法
- 随机获取
- 根据pid列表查询
2.1Controller
/**
* 查看推荐动态:
* 1.请求路径:/movements/recommend
* 2.请求方式:get
* 3.请求参数:page,当前页码;pageSize,每页展示数
* 4.响应数据:MovementsVO
*/
@GetMapping("/recommend")
public ResponseEntity lookRecommendMoments(@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer pageSize ){
//1.调用业务层完成业务逻辑
PageResult pageResult = movementsService.lookRecommendMoments(page,pageSize);
//2.构造返回值
return ResponseEntity.ok(pageResult);
}
2.2Service
/**
*
* 查看大数据系统推荐的动态
*/
public PageResult lookRecommendMoments(Integer page, Integer pageSize) {
//1.从Redis中获取大数据推荐系统要推送的动态详情存入的动态详情pid
//1.1先获取当前登录的用户id
Long userId = ThreadLocalUtils.getUserId();
String redisKey = Constants.MOVEMENTS_RECOMMEND + userId;
String redisValue = redisTemplate.opsForValue().get(redisKey);
//2.判断大数据推荐系统有没有推荐动态详情
// 判断redisValue是否为空,为空则自己随机推荐几条动态详情给用户
List<Movement> movements = Collections.EMPTY_LIST;
if (StringUtils.isEmpty(redisValue)){
movements = movementsApi.randomMoments(pageSize);
}else{
//3.大数据系统根据用户的行为特征计算出要推送给用户的动态详情
//处理pid
String[] pids = redisValue.split(",");
if( (page - 1) * pageSize < pids.length) {
//这两行没有注释,因为我自己也不怎么理解
List<Long> longPids = Arrays.stream(pids).skip((page - 1) * pageSize).limit(pageSize)
.map(e -> Long.valueOf(e))
.collect(Collectors.toList());
//4.调用api完成批量查询动态详情
movements =movementsApi.lookRecommendMoments(longPids);
}
}
//5.先从查询出来的要推荐给用户的动态详情集合里获取全部用户id
//调用UserInfo查询全部用户的用户详情
List<Long> userIds = CollUtil.getFieldValues(movements, "userId", Long.class);
Map<Long, UserInfo> userInfoMap = userInfoApi.batchQueryUserInfo(userIds, null);
//6.遍历查出来要推荐给用户的动态详情集合,每一个动态详情集合构建一个vo对象
List<MovementsVo> movementsVos = new ArrayList<>();
for (Movement movement : movements) {
//获取用户id
Long id = movement.getUserId();
//根据用户id获取用户详情
UserInfo userInfo = userInfoMap.get(id);
if(userInfo != null){
MovementsVo movementsVo = MovementsVo.init(userInfo, movement);
String key = Constants.MOVEMENTS_INTERACT_KEY + movement.getId().toHexString();
String hashKey = Constants.MOVEMENT_LIKE_HASHKEY + ThreadLocalUtils.getUserId();
if(redisTemplate.opsForHash().hasKey(key, hashKey)){
movementsVo.setHasLiked(1);
}
String loveHashKey = Constants.MOVEMENT_LOVE_HASHKEY + ThreadLocalUtils.getUserId();
if(redisTemplate.opsForHash().hasKey(key, loveHashKey)){
movementsVo.setHasLoved(1);
}
movementsVos.add(movementsVo);
}
}
//7.构造返回值
PageResult pageResult = new PageResult(page, pageSize ,0,movementsVos);
return pageResult;
}
2.3ApiC层
/**
* 大数据推荐系统没有推荐的动态详情,自己随机推荐,保证用户使用体验
*/
public List<Movement> randomMoments(Integer pageSize) {
//
TypedAggregation<Movement> aggregation = Aggregation.newAggregation(Movement.class, Aggregation.sample(pageSize));
AggregationResults<Movement> results = mongoTemplate.aggregate(aggregation, Movement.class);
List<Movement> mappedResults = results.getMappedResults();
return mappedResults;
}
/**
* 大数据系统根据用户的行为特征计算出要推送给用户的动态详情
*/
public List<Movement> lookRecommendMoments(List<Long> longPids) {
//1.创建Criteria对象,设置查询条件,根据pid查询动态详情表
Criteria criteria = Criteria.where("pid").in(longPids);
//2.根据criteria创建Query对象
Query query = Query.query(criteria);
List<Movement> movements = mongoTemplate.find(query, Movement.class);
return movements;
}
三、查询单条动态
一、 Controller
/**
* 根据动态详情对象的id查询单条用户动态:
* 请求路径:/movements/:id
* 请求方式:get
* 请求参数:路径参数id,动态id
* 响应数据:MovementsVo
*/
@GetMapping("/{id}")
public ResponseEntity lookOneMoment(@PathVariable("id") String id){
//1.调用业务层完成业务逻辑
MovementsVo movementsVo = movementsService.lookOneMoment(id);
//2.构建返回数据
return ResponseEntity.ok(movementsVo);
}
二、Service
/**
*
* 查看单条动态
*/
public MovementsVo lookOneMoment(String movementId) {
//1.调用api查询动态
Movement movement = movementsApi.lookOneMoment(movementId);
MovementsVo movementsVo =null;
//2.非空校验
if (movement == null) {
return null;
} else {
//调用userInfoApi查用户详情
UserInfo userInfo = userInfoApi.selectUserInfo(movement.getUserId());
if (userInfo != null) {
movementsVo = MovementsVo.init(userInfo, movement);
String key = Constants.MOVEMENTS_INTERACT_KEY + movement.getId().toHexString();
String hashKey = Constants.MOVEMENT_LIKE_HASHKEY + ThreadLocalUtils.getUserId();
if (redisTemplate.opsForHash().hasKey(key, hashKey)) {
movementsVo.setHasLiked(1);
movementsVo.setLikeCount(1);
}
String loveHashKey = Constants.MOVEMENT_LOVE_HASHKEY + ThreadLocalUtils.getUserId();
if (redisTemplate.opsForHash().hasKey(key, loveHashKey)) {
movementsVo.setHasLoved(1);
}
}
}
return movementsVo;
}
三、api
/**
*
*查询单条动态
*/
public Movement lookOneMoment(String movementId) {
Movement movement = mongoTemplate.findById(movementId, Movement.class);
return movement;
}