探花交友圈子

需求分析

和微信朋友圈类似,用户可以查看自己好友发布的动态。查询过程我们在表设计的时候就已经说过了,这里不再赘述。需要注意的是,这里也需要返回动态的VO对象。下面是接口:

api

  • 根据用户id到时间线表中查询所有符合条件的动态id,注意这里查询的条件是friendId = 传递的用户id 好友的朋友是你

  • 根据动态id查询到所有的动态

  • 根据动态中的作者的用户id,到MySQL的UserInfo表中查询用户详情

  • 最终将两部分封装成VO返回

查询好友动态时,我们可以根据好友动态时间线表查询
因为好友发表动态时都会在时间表存储数据,把好友信息,和发布的动态id存储
我们就可以根据自己的userID对应friendId查询出好友动态id
再根据动态id查询好友动态,封装数据返回

MovementsController

/**
 * 查询好友动态
 * GET  /movements
 */
@GetMapping
public ResponseEntity findFriendMovement(@RequestParam(defaultValue = "1") Integer page,
                                         @RequestParam(defaultValue = "10") Integer pagesize) {
    PageResult pageResult = movementsService.findFriendMovement(page, pagesize);
    return ResponseEntity.ok(pageResult);
}

MovementsService

public PageResult findFriendMovement(Integer page, Integer pagesize) {
    //根据用户id到时间线表中查询所有符合条件的动态id,注意这里查询的条件是friendId = 传递的用户id  好友的朋友是你
   //查询当前用户id
    Long userId = UserHolder.getUserId();
    List<Movement> movementList = movementApi.findByFriendId(userId, page, pagesize);
 // 3. 从集合中获取到发送动态的所有的好友id
   //查看动态表中的数据是否为空,空的话返回一个空的集合
    if (CollUtil.isEmpty(movementList)) {
        return new PageResult();
    }
   //不为空通过动态表,去完成用户详情信息的封装
    List<MovementsVo> voList = createMovementVo(movementList);

    return getPageResult(page, pagesize, voList);
}

MovementApiImpl

// 根据时间线查询好友动态
 @Override
 public List<Movement> findByFriendId(Long userId, Integer page, Integer pagesize) {
     Query query=Query.query(Criteria.where("friendId").is(userId))//好友的朋友是你
             .skip((page-1)*pagesize)
             .limit(pagesize)
             .with(Sort.by(Sort.Order.desc("created")));//分页排序,mongodb
    //根据时间线表中的friendid ==userId ,来查询动态id
     List<MovementTimeLine> timeLineList = mongoTemplate.find(query, MovementTimeLine.class);
     //根据时间线集合中的movementId来查询集合中的动态id 注意类型的转化
     List<ObjectId> movementIds = CollUtil.getFieldValues(timeLineList, "movementId", ObjectId.class);
    //通过动态id,查询动态表中的数据
     Query qw =Query.query(Criteria.where("id").in(movementIds));
     List<Movement> movementList = mongoTemplate.find(qw, Movement.class);
     return movementList;
 }

createMovementVo,查询出用户的详情信息,通过封装vo

private List<MovementsVo> createMovementVo(List<Movement> movementList) {
    if (CollUtil.isEmpty(movementList)) {
        // 如果为动态为空,那么说明已经到底了 那么直接返回一个空的vo集合即可
        List<MovementsVo> list = new ArrayList<>();
        return list;
    } else {
        List<Long> friendIds = CollUtil.getFieldValues(movementList, "userId", Long.class);
        // 4. 根据id查询好友的详细信息,得到一个集合
       //getUserInfoByIds写过此方法
        Map<Long, UserInfo> friendUserInfo = this.userInfoApi.getUserInfoByIds(friendIds, null);
        // 5. 遍历动态集合,封装vo
        List<MovementsVo> voList = new ArrayList<>();
        for (Movement movement : movementList) {
            // 获取到动态发布者的id
            Long friendId = movement.getUserId();
            UserInfo userInfo = friendUserInfo.get(friendId);
            if (userInfo != null) {
               //内部方法进行封装,转换数据类型
                MovementsVo init = MovementsVo.init(userInfo, movement);
                voList.add(init);
            }
        }
        return voList;
    }
}
查询推荐动态

需求分析

基于大数据,探花交友APP会根据用户的喜欢等信息推荐出用户可能感兴趣的动态。具体推荐由大数据系统负责,我们不需要关心。我们只需要知道,推荐信息保存在Redis中,保存的形式是如下:

采用String类型保存数据

Key为MOVEMENTS_RECOMMEND_用户id

Value为动态的pid,多个pid之间用逗号隔开

因此我们的业务流程如下:

前端传来请求,携带分页参数

后端获取到用户id,根据用户id到Redis中查询推荐信息。如果在Redis中查询不到推荐信息,则从数据库中随机生成pageSize个动态。

然后根据pid进行分页,获取当期页的所有pid

根据pid获取到动态集合,并且根据动态中的作者信息,获取到动态的作者详情

封装VO

api参数

分析:
查询推荐动态时.我们要先在redis中查询推荐动态的pid
如果redis有数据我们就要解析数据("16,17,18,19,20,21,10015,10020,10040,10064,10092,10093,10099,10067")
利用stream流把数据分成一段段的列表
如果redis中没有数据就在数据库构造十条(随机构造)
构造十条数据时我们要用到TypedAggregation对象

代码实现

问题1:需要掌握以下两个问题,第一个是我们从Redis中获取到的推荐的pid是一个字符串,如何实现分页?

我们可以split方法先转化成数组,然后利用Stream流中的skip()和limit()方法实现分页 java8中的新特性

如何从MongoDB中随机获取指定数量的动态

利用MongoDB中的聚合函数。Aggregation.smple()设置随机采样数

MovementsController

/**
 * 推荐动态
 * GET  /movements/recommend
 */
@GetMapping("/recommend")
public ResponseEntity findRecommendMovement(@RequestParam(defaultValue = "1") Integer page,
                                            @RequestParam(defaultValue = "10") Integer pagesize) {
    PageResult pageResult = movementsService.findRecommendMovement(page, pagesize);
    return ResponseEntity.ok(pageResult);
}

MovementsService

public PageResult findRecommendMovement(Integer page, Integer pagesize) {
    //从redis中获取数据
    //String redisKey="MOVEMENTS_RECOMMEND_"+UserHolder.getUserId();
   //根据用户Id 到redis中查询数据 判断推荐信息是否存在
    String redisKey = Constants.MOVEMENTS_RECOMMEND + UserHolder.getUserId();
    String redisValue = redisTemplate.opsForValue().get(redisKey);
    //判断数据是否存在
    List<Movement> list = Collections.EMPTY_LIST;//构建空集合
    if (StringUtils.isEmpty(redisValue)) {
        //如果不存在调用api构造十条数据,则随机生成记录
        list = movementApi.randomMonements(pagesize);
    } else {
        //如果存在处理pid数据
        String[] pids = redisValue.split(",");
        if ((page - 1) * pagesize < pids.length) {
            List<Long> pids = Arrays.stream(pids)
                    .skip((page - 1) * pagesize)
                    .limit(pagesize)
                    .map(e -> Long.valueOf(e))
                    .collect(Collectors.toList());
            list = movementApi.findMovementByPid(pids);
        }
    }
 // 4. 封装vo
    List<MovementsVo> movementsVoList = createMovementVo(movementList);
    // // 5. 封装返回值
    return new PageResult(page, pagesize, 0, movementsVoList)
}

MovementApiImpl

@Override
public List<Movement> randomMonements(Integer pagesize) {
    //创建通缉对象
    TypedAggregation aggregation= Aggregation.newAggregation(Movement.class,Aggregation.sample(pagesize));
    //调用mongoTemepalte统计
    AggregationResults<Movement> results = mongoTemplate.aggregate(aggregation, Movement.class);
    return results.getMappedResults();
}

@Override
public List<Movement> findMovementByPid(List<Long> pids) {
    Criteria criteria=Criteria.where("pid").in(pids);
    Query query=Query.query(criteria);
    List<Movement> list = mongoTemplate.find(query, Movement.class);
    return list;
}
import cn.hutool.core.collection.CollUtil;
import com.sun.xml.internal.ws.policy.privateutil.PolicyUtils;
import com.tanhua.autoconfig.template.OssTemplate;
import com.tanhua.dubbo.api.CommentApi;
import com.tanhua.dubbo.api.MovementApi;
import com.tanhua.dubbo.api.UserInfoApi;
import com.tanhua.dubbo.api.VisitorsApi;
import com.tanhua.model.domain.UserInfo;
import com.tanhua.model.enums.CommentType;
import com.tanhua.model.mongo.Comment;
import com.tanhua.model.mongo.Movement;
import com.tanhua.model.mongo.Visitors;
import com.tanhua.model.vo.ErrorResult;
import com.tanhua.model.vo.MovementsVo;
import com.tanhua.model.vo.PageResult;
import com.tanhua.model.vo.VisitorsVo;
import com.tanhua.server.exception.BusinessException;
import com.tanhua.server.interceptor.UserHolder;
import com.tanhua.utils.Constants;
import org.apache.dubbo.config.annotation.DubboReference;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class MovementService {

    @Autowired
    private OssTemplate ossTemplate;

    @DubboReference
    private MovementApi movementApi;

    @DubboReference
    private UserInfoApi userInfoApi;

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @DubboReference
    private CommentApi commentApi;

    @DubboReference
    private VisitorsApi visitorsApi;

    /**
     * 当前用户发布动态
     *
     * @param movement
     * @param imageContent
     */
    public void movements(Movement movement, MultipartFile[] imageContent) throws IOException {
        //判断用户是否输入文本信息
        if (StringUtils.isEmpty(movement.getTextContent())) {
            throw new BusinessException(ErrorResult.disloveError());
        }
        //获取当前用户id
        Long userId = UserHolder.getUserId();
        //将图片存入oss中
        List<String> medias = new ArrayList<>();
        for (MultipartFile multipartFile : imageContent) {
            String url = ossTemplate.uploadMovement(multipartFile.getOriginalFilename(), multipartFile.getInputStream(), userId);
            medias.add(url);
        }
        movement.setMedias(medias);
        movement.setUserId(userId);
        //调用mongoDbApi存入数据并存入时间线表
        movementApi.publishMovements(movement);
    }

    /**
     * 查询当前用户id的所有动态
     *
     * @param userId
     * @param page
     * @param pagesize
     * @return
     */
    public PageResult findByIdMovements(Long userId, int page, int pagesize) {
        //1.调用mongoApi查询当前用户的所有动态信息
        PageResult pageResult = movementApi.findById(userId, page, pagesize);

        List<Movement> items = (List<Movement>) pageResult.getItems();
        //如果没有数据直接返回pageResult对象
        if (items == null || items.size() <= 0) {
            return pageResult;
        }

        //根据id查询用户详情信息
        UserInfo userInfo = userInfoApi.findById(userId);

        List<MovementsVo> list = new ArrayList<>();

        for (Movement item : items) {
            MovementsVo movementsVo = MovementsVo.init(userInfo, item);
            list.add(movementsVo);
        }

        pageResult.setItems(list);

        return pageResult;
    }

    /**
     * 查询当前用户所有好友动态
     * @param page
     * @param pagesize
     * @return
     */
    public PageResult findByFriendMovementAll(int page, int pagesize) {
        //获取当前用户id
        Long userId = UserHolder.getUserId();
        //调用mongoApi接口获取所有好友的动态信息
        List<Movement> movementList = movementApi.findByIdFriendMovementAll(userId, page, pagesize);

        return getPageResult(page,pagesize,movementList);

    }

    /**
     * 查询当前用户推荐好友的所有动态
     * @param page
     * @param pagesize
     * @return
     */
    public PageResult findByRecommendM(int page, int pagesize) {
        Long userId = UserHolder.getUserId();
        //- 从redis获取当前用户的推荐PID列表
        String redisValue = redisTemplate.opsForValue().get(Constants.MOVEMENTS_RECOMMEND + userId);

        List<Movement> movementList = Collections.EMPTY_LIST;

        if (StringUtils.isEmpty(redisValue)){
            //- 如果不存在,调用API随机获取10条动态数据
            movementList = movementApi.randomRecommendM(pagesize);
        }else {
            //- 如果存在,调用API根据PID列表查询动态数据
            //- 构造VO对象
            String[] split = redisValue.split(",");
            if ((page - 1) * pagesize < split.length ){
                List<Long> pidList = Arrays.stream(split).skip((page - 1) * pagesize).limit(pagesize)
                        .map(item->Long.valueOf(item))
                        .collect(Collectors.toList());
                //调用Api查询推荐好友的所有动态
                movementList = movementApi.findMovementByPid(pidList);
            }
        }
        return getPageResult(page,pagesize,movementList);
    }

    //获取PageResult对象
    private PageResult getPageResult(int page, int pagesize, List<Movement> movementList) {
        if (CollUtil.isEmpty(movementList)){
            return new PageResult();
        }

        Set<Long> userIds = movementList.stream().map(item -> item.getUserId()).collect(Collectors.toSet());

        Map<Long, UserInfo> map = userInfoApi.findByIds(new UserInfo(), new ArrayList<>(userIds));

        List<MovementsVo> vos = new ArrayList<>();

        for (Movement movement : movementList) {
            UserInfo userInfo = map.get(movement.getUserId());
            if (userInfo != null){
                MovementsVo vo = MovementsVo.init(userInfo, movement);
                //从Redis中判断是否点赞过该评论
                String Key = Constants.MOVEMENTS_INTERACT_KEY + movement.getId().toHexString();
                String likeHashKey = Constants.MOVEMENT_LIKE_HASHKEY + UserHolder.getUserId();
                if (redisTemplate.opsForHash().get(Key, likeHashKey) != null){
                    vo.setHasLiked(1);
                }
                //从Redis中判断是否喜欢过该评论
                String loveHashKey = Constants.MOVEMENT_LOVE_HASHKEY + movement.getId().toHexString();
                if (redisTemplate.opsForHash().get(Key, loveHashKey) != null){
                    vo.setHasLoved(1);
                }
                vos.add(vo);
            }
        }
        return new PageResult(page,pagesize,0,vos);
    }

    /**
     * 根据动态id获取单条动态
     * @param movementId
     * @return
     */
    public MovementsVo findMovement(String movementId) {
        //调用API获取当前动态id的所有动态
        Movement movement = movementApi.findMovement(movementId);

        if (movement == null){
            return null;
        }
        UserInfo userInfo = userInfoApi.findById(movement.getUserId());

        return MovementsVo.init(userInfo, movement);
    }

    /**
     * 动态-点赞
     * @param movementId
     * @return
     */
    public Integer saveLike(String movementId) {
        //判断是否点赞过该评论
        boolean exists = commentApi.findComment(movementId, CommentType.LIKE,UserHolder.getUserId());

        if (exists){
            throw  new BusinessException(ErrorResult.likeError());
        }
        //调用API获取点赞总数
        Comment comment = new Comment();
        comment.setPublishId(new ObjectId(movementId));
        comment.setCommentType(CommentType.LIKE.getType());
        comment.setUserId(UserHolder.getUserId());
        comment.setCreated(System.currentTimeMillis());

        Integer countLikes = commentApi.saveComments(comment);
        //4、拼接redis的key,将用户的点赞状态存入redis
        String key = Constants.MOVEMENTS_INTERACT_KEY + movementId;
        String hashKey = Constants.MOVEMENT_LIKE_HASHKEY + UserHolder.getUserId();

        redisTemplate.opsForHash().put(key,hashKey,"1");

        return countLikes;
    }

    /**
     * 动态取消点赞
     * @param movementId
     * @return
     */
    public Integer disLike(String movementId) {
        boolean exists = commentApi.findComment(movementId, CommentType.LIKE, UserHolder.getUserId());

        if (!exists){
            throw  new BusinessException(ErrorResult.disLikeError());
        }

        //调用API获取点赞总数
        Comment comment = new Comment();
        comment.setPublishId(new ObjectId(movementId));
        comment.setCommentType(CommentType.LIKE.getType());
        comment.setUserId(UserHolder.getUserId());

        Integer countLikes = commentApi.delete(comment);

        //删除redis中的点赞标记
        String key = Constants.MOVEMENTS_INTERACT_KEY + movementId;
        String hashKey = Constants.MOVEMENT_LIKE_HASHKEY + UserHolder.getUserId();

        redisTemplate.opsForHash().delete(key,hashKey);

        return countLikes;
    }

    /**
     * 动态-喜欢
     * @param movementId
     * @return
     */
    public Integer saveLove(String movementId) {
        //判断是否喜欢过该评论
        boolean exists = commentApi.findComment(movementId,CommentType.LOVE,UserHolder.getUserId());

        if (exists){
            throw  new BusinessException(ErrorResult.likeError());
        }
        //调用API获取喜欢总数
        Comment comment = new Comment();
        comment.setPublishId(new ObjectId(movementId));
        comment.setCommentType(CommentType.LOVE.getType());
        comment.setUserId(UserHolder.getUserId());
        comment.setCreated(System.currentTimeMillis());

        Integer countLikes = commentApi.saveComments(comment);
        //4、拼接redis的key,将用户的喜欢状态存入redis
        String key = Constants.MOVEMENTS_INTERACT_KEY + movementId;
        String hashKey = Constants.MOVEMENT_LOVE_HASHKEY + UserHolder.getUserId();

        redisTemplate.opsForHash().put(key,hashKey,"1");

        return countLikes;

    }

    /**
     * 动态-取消喜欢
     * @param movementId
     * @return
     */
    public Integer unlove(String movementId) {
        boolean exists = commentApi.findComment(movementId, CommentType.LOVE, UserHolder.getUserId());

        if (!exists){
            throw  new BusinessException(ErrorResult.disLikeError());
        }

        //调用API获取喜欢总数
        Comment comment = new Comment();
        comment.setPublishId(new ObjectId(movementId));
        comment.setCommentType(CommentType.LOVE.getType());
        comment.setUserId(UserHolder.getUserId());

        Integer countLikes = commentApi.delete(comment);

        //删除redis中的喜欢标记
        String key = Constants.MOVEMENTS_INTERACT_KEY + movementId;
        String hashKey = Constants.MOVEMENT_LOVE_HASHKEY + UserHolder.getUserId();

        redisTemplate.opsForHash().delete(key,hashKey);

        return countLikes;
    }

    /**
     * 谁看过我
     * @return
     */
    public List<VisitorsVo> visitors() {
        //查询访问的时间
        String key = Constants.VISITORS_USER;
        String hashKey = UserHolder.getUserId().toString();
        String value = (String) redisTemplate.opsForHash().get(key, hashKey);
        Long data = StringUtils.isEmpty(value)?null:Long.valueOf(value);
        //调用API判断今天看过我的用户
        List<Visitors> list = visitorsApi.lookVisitors(data,UserHolder.getUserId());
        if (CollUtil.isEmpty(list)){
            return new ArrayList<>();
        }
        //提取用户id
        List<Long> ids = list.stream().map(item -> item.getVisitorUserId()).collect(Collectors.toList());
        //调用APi获取用户信息
        Map<Long, UserInfo> map = userInfoApi.findByIds(null, ids);
        List<VisitorsVo> vos = new ArrayList<>();
        for (Visitors visitors : list) {
            UserInfo userInfo = map.get(visitors.getVisitorUserId());
            if (userInfo != null){
                VisitorsVo vo = VisitorsVo.init(userInfo, visitors);
                vos.add(vo);
            }
        }
        return vos;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值