朋友圈基础功能

需求分析

探花交友项目中的圈子功能,类似于朋友圈,基本功能为:发布动态,浏览好友动态,浏览推荐动态,点赞、评论、喜欢、关注、等功能

发布动态:选择照片以及输入动态文字,点击发送按钮

好友:需要看见你的动态;陌生人:也可能看见你的动态(推荐系统分析给你推送)

 发布动态表:往此表中插入动态数据(发布id,动态id-主键id)

好友关系表

好友时间线表:当前用户好友的动态数据表

相册表:当前用户自己的动态数据表

技术方案

采用mongodb+redis来实现,其中mongodb负责存储,redis负责缓存

发布动态,:发布表-保存动态数据、往相册表-保存我的动态,好友表-查询好友id、往好友时间线表-保存好友动态

查询好友动态:好友时间线表:根据当前用户id拼接集合名、发布表、用户信息表

查询推荐动态:推荐动态表:根据当前用户id查询推荐动态表获取到发布id、发布表、用户信息表

实现方案分析

所以对于存储而言,主要是核心的6张表:

  • 发布表:记录了所有用户的发布的东西信息,如图片、视频等。

  • 相册:相册是每个用户独立的,记录了该用户所发布的所有内容。

  • 评论:针对某个具体发布的朋友评论和点赞操作。

  • 时间线:所谓“刷朋友圈”,就是刷时间线,就是一个用户所有的朋友的发布内容。

  • 好友表:记录好友关系

  • 推荐圈子表:记录推荐动态表

 发布流程

 首先用户发布动态,将动态内容写入到发布表

然后,将发布的指向写入到自己的相册表中

最后,将发布的指向写入到好友的时间线中。

 

查看流程

 

流程说明:

  • 用户查看动态,如果查看自己的动态,直接查询相册表即可

  • 如果查看好友动态,查询时间线表即可

  • 如果查看推荐动态,查看推荐表即可

由此可见,查看动态的成本较低,可以快速的查询到动态数据。

数据库表分析

发布表

#表名:quanzi_publish
{
    "id":1,#主键id
    "userId":1, #用户id
    "text":"今天心情很好", #文本内容
    "medias":"http://xxxx/x/y/z.jpg", #媒体数据,图片或小视频 url
    "seeType":1, #谁可以看,1-公开,2-私密,3-部分可见,4-不给谁看
    "seeList":[1,2,3], #部分可见的列表
    "notSeeList":[4,5,6],#不给谁看的列表
    "longitude":108.840974298098,#经度
    "latitude":34.2789316522934,#纬度
    "locationName":"上海市浦东区", #位置名称
    "created",1568012791171 #发布时间
}

 

相册表

#表名:quanzi_album_{userId}
{
    "id":1,#主键id
    "publishId":1001, #发布id
    "created":1568012791171 #发布时间
}

 

时间线表

#表名:quanzi_time_line_{userId}
{
    "id":1,#主键id,
    "userId":2, #好友id
    "publishId":1001, #发布id
    "date":1568012791171 #发布时间
}

评论表

#表名:quanzi_comment
{
    "id":1, #主键id
    "publishId":1001, #发布id
    "commentType":1, #评论类型,1-点赞,2-评论,3-喜欢
    "content":"给力!", #评论内容
    "userId":2, #评论人
    "isParent":false, #是否为父节点,默认是否
    "parentId":1001, #父节点id
    "created":1568012791171
}

 好友表

#表名:tanhua_users
{
    "id":1, #主键id
    "userId":1001, #用户id
    "friendId":1,  #好友id
    "created":1568012791171
}

 推荐动态表

 #表名:recommend_quanzi
{
    "id" : 1, #主键id
    "userId" : 1001, #用户id
    "score" : 9.0,
    "created" : 1568012791171,  
    "publishId":1001, #发布id
}

 实体类与vo

tanhua-domain模块mongo包中创建以下实体对象

Publish

package com.tanhua.domain.mongo;

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

import java.io.Serializable;
import java.util.List;

/**
 * <p>
 * 发布的动态信息表
 * </p>
 */
@Data
@Document(collection = "quanzi_publish")
public class Publish implements Serializable {

    private ObjectId id; //主键id
    private Long pid; //Long类型,用于推荐系统的模型
    private Long userId;
    private String textContent; //文字

    private List<String> medias; //媒体数据,图片或小视频 url
    private Integer seeType; // 谁可以看,1-公开,2-私密,3-部分可见,4-不给谁看

    private String longitude; //经度
    private String latitude; //纬度
    private String locationName; //位置名称
    private Long created; //发布时间

    private Integer likeCount=0; //点赞数
    private Integer commentCount=0; //评论数
    private Integer loveCount=0; //喜欢数
}

Album

package com.tanhua.domain.mongo;

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

import java.io.Serializable;

/**
 * <p>
 * 相册表,用于存储自己发布的数据,每一个用户一张表进行存储
 * </p>
 */
@Data
public class Album implements Serializable {

    private ObjectId id; //主键id
    private ObjectId publishId; //发布id
    private Long created; //发布时间
}

TimeLine

package com.tanhua.domain.mongo;

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

import java.io.Serializable;

/**
 * <p>
 * 时间线表,用于存储发布(或推荐)的数据,每一个用户一张表进行存储
 * </p>
 */
@Data
public class TimeLine implements Serializable {

    private ObjectId id;

    private Long userId; // 好友id
    private ObjectId publishId; //发布id

    private Long created; //发布的时间
}

Friend

package com.tanhua.domain.mongo;

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

import java.io.Serializable;

/**
 * <p>
 * 好友关系
 * </p>
 */
@Data
@Document(collection = "tanhua_users")
public class Friend implements Serializable {
    private ObjectId id;
    private Long userId; //用户id
    private Long friendId; //好友id
    private Long created; //时间
}

RecommendQuanzi

package com.tanhua.domain.mongo;

import lombok.Data;
import org.bson.types.ObjectId;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;

import java.io.Serializable;

/**
 * <p>
 * 推荐动态
 * </p>
 */
@Data
@Document(collection = "recommend_quanzi")
public class RecommendQuanzi implements Serializable {

    @Id
    private ObjectId id; // 主键
    @Indexed
    private Long userId; // 推荐的用户id
    private Long pid;
    private ObjectId publishId; // 发布的动态的id
    @Indexed
    private Double score = 0d; // 推荐分数
    private Long created; // 日期
}

PublishVo

package com.tanhua.domain.vo;
import lombok.Data;
import java.io.Serializable;
import java.util.List;

@Data
public class PublishVo implements Serializable {

    private Long userId; // 用户id
    private String textContent; // 文本内容
    private String location; // 地理位置
    private String longitude; // 经度
    private String latitude; // 纬度
    private List<String> medias; // 图片url
}

MomentVo

package com.tanhua.domain.vo;
import lombok.Data;
import java.io.Serializable;
@Data
public class MomentVo implements Serializable {
    private String id; //动态id

    private Long userId; //用户id
    private String avatar; //头像
    private String nickname; //昵称
    private String gender; //性别 man woman
    private Integer age; //年龄
    private String[] tags; //标签


    private String textContent; //文字动态
    private String[] imageContent; //图片动态
    private String distance; //距离
    private String createDate; //发布时间 如: 10分钟前
    private int likeCount; //点赞数
    private int commentCount; //评论数
    private int loveCount; //喜欢数


    private Integer hasLiked; //是否点赞(1是,0否)
    private Integer hasLoved; //是否喜欢(1是,0否)
}

app发送请求,服务消费者接收请求,发布动态保存到动态表,保存到自己的相册表(动态表)根据登录用户id来查询好友id,再将发布信息保存到时间线表中

服务端

PublishApi
package com.tanhua.dubbo.api.mongo;

import com.tanhua.domain.mongo.Publish;
import com.tanhua.domain.vo.PageResult;
import com.tanhua.domain.vo.PublishVo;

/**
 * 圈子服务接口
 */
public interface PublishApi {
    /**
     * 发布动态
     */
    String savePublish(PublishVo publishVo);

    /**
     * 好友动态
     */
    PageResult<Publish> queryPublishByPage(int page, int pagesize, Long userId);

    /**
     * 推荐动态(陌生人动态)
     */
    PageResult<Publish> queryPublishByRecommendQuanzi(int page, int pagesize, Long userId);

    /**
     * 用户动态
     * 1.首页推荐用户列表-点击用户进入会查询用户动态
     * 2.我的模块-我的动态-查看当前用户动态
     */
    PageResult<Publish> queryPublishByAlbum(int page, int pagesize, Long userId);

    /**
     * 单条动态查询
     */
    Publish querySinglePublish(String publishId);

    /**
     * 根据发布id更新动态状态
     * @param publishId
     * @param state
     */
    void updatePublish(String publishId, Integer state);
}
PublishApiImpl
package com.tanhua.dubbo.api.mongo;

import com.tanhua.domain.mongo.*;
import com.tanhua.domain.vo.PageResult;
import com.tanhua.domain.vo.PublishVo;
import org.apache.dubbo.config.annotation.Service;
import org.bson.types.ObjectId;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
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 org.springframework.data.mongodb.core.query.Update;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

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

/**
 * 圈子服务实现类
 *      发布动态
 *      好友动态
 *      推荐动态
 */
@Service
public class PublishApiImpl implements PublishApi{

    @Autowired
    private MongoTemplate mongoTemplate;


    /**
     * 发布动态
     */
    @Override
    public String savePublish(PublishVo publishVo) {
        Long currentUserId = publishVo.getUserId();//当前用户id
        long nowTime = System.currentTimeMillis();
        //1.发布动态表-保存
        Publish publish = new Publish();
        BeanUtils.copyProperties(publishVo,publish);//userId textContent longitude latitude medias
        publish.setId(ObjectId.get());//手动设置id
        publish.setPid(1l);//没有用到
        publish.setSeeType(1);//公开
        publish.setLocationName(publishVo.getLocation());//位置名称
        publish.setCreated(nowTime);//发布动态时间
        mongoTemplate.save(publish);
        //2.相册表-保存  quanzi_album_2
        Album album = new Album();
        album.setPublishId(publish.getId());//发布id
        album.setCreated(nowTime);//创建时间
        mongoTemplate.save(album,"quanzi_album_"+currentUserId);//我的相册表
        //3.好友表-查询好友ids tanhua_users(异步处理-进行解耦-MQ)
        Query queryFriend = new Query();
        queryFriend.addCriteria(Criteria.where("userId").is(currentUserId));
        List<Friend> friendList = mongoTemplate.find(queryFriend, Friend.class);
        if(!CollectionUtils.isEmpty(friendList)){
            for (Friend friend : friendList) {
                Long friendId = friend.getFriendId();//好友id
                //4.好友时间线表-保存 quanzi_time_line_1
                TimeLine timeLine = new TimeLine();
                timeLine.setUserId(currentUserId);//好友id
                timeLine.setPublishId(publish.getId());//发布id
                timeLine.setCreated(nowTime);//创建时间
                mongoTemplate.save(timeLine,"quanzi_time_line_"+friendId);//好友时间线表
            }
        }
        return publish.getId().toHexString();
    }

    /**
     * 好友动态
     */
    @Override
    public PageResult<Publish> queryPublishByPage(int page, int pagesize, Long userId) {
        //1.分页查询时间线表
        Query query = new Query();
        query.limit(pagesize).skip((page-1)*pagesize);
        query.with(Sort.by(Sort.Direction.DESC,"created"));//降序 将时间最新的在最上面展示
        long counts = mongoTemplate.count(query, "quanzi_time_line_" + userId);
        List<TimeLine> timeLineList = mongoTemplate.find(query,TimeLine.class,"quanzi_time_line_" + userId);
        //2.根据时间线表中 发布id  查询发布表数据
        //将 List<TimeLine>转为List<Publish>
        List<Publish> publishList = new ArrayList<>();
        if(!CollectionUtils.isEmpty(timeLineList)){
            for (TimeLine timeLine : timeLineList) {
                ObjectId publishId = timeLine.getPublishId();//发布id
                Publish publish = mongoTemplate.findById(publishId, Publish.class);
                if(!StringUtils.isEmpty(publish)) {
                    publishList.add(publish);
                }
            }
        }
        //3.封装分页对象返回
        long pages = (counts / pagesize) + (counts%pagesize > 0 ? 1:0);
        return new PageResult<>(counts,(long)pagesize,pages,(long)page,publishList);
    }

    /**
     * 推荐动态(陌生人动态)
     */
    @Override
    public PageResult<Publish> queryPublishByRecommendQuanzi(int page, int pagesize, Long userId) {
        //1.分页查询推荐动态表
        Query query = new Query();
        query.limit(pagesize).skip((page-1)*pagesize);
        query.addCriteria(Criteria.where("userId").is(userId));//根据userId查询
        query.with(Sort.by(Sort.Direction.DESC,"created"));//降序 将时间最新的在最上面展示
        long counts = mongoTemplate.count(query, RecommendQuanzi.class);
        List<RecommendQuanzi> recommendQuanziList = mongoTemplate.find(query,RecommendQuanzi.class);
        //2.根据时间线表中 发布id  查询发布表数据
        //将 List<RecommendQuanzi>转为List<Publish>
        List<Publish> publishList = new ArrayList<>();
        if(!CollectionUtils.isEmpty(recommendQuanziList)){
            for (RecommendQuanzi recommendQuanzi : recommendQuanziList) {
                ObjectId publishId = recommendQuanzi.getPublishId();//发布id
                Publish publish = mongoTemplate.findById(publishId, Publish.class);
                if(!StringUtils.isEmpty(publish)) {
                    publishList.add(publish);
                }
            }
        }
        //3.封装分页对象返回
        long pages = (counts / pagesize) + (counts%pagesize > 0 ? 1:0);
        return new PageResult<>(counts,(long)pagesize,pages,(long)page,publishList);
    }

    /**
     * 用户动态
     * 1.首页推荐用户列表-点击用户进入会查询用户动态
     * 2.我的模块-我的动态-查看当前用户动态
     */
    @Override
    public PageResult<Publish> queryPublishByAlbum(int page, int pagesize, Long userId) {
        //1.分页查询相册表
        Query query = new Query();
        query.limit(pagesize).skip((page-1)*pagesize);
        query.with(Sort.by(Sort.Direction.DESC,"created"));//降序 将时间最新的在最上面展示
        long counts = mongoTemplate.count(query, "quanzi_album_"+userId);//
        List<Album> albumList = mongoTemplate.find(query,Album.class,"quanzi_album_"+userId);
        //2.根据相册表中的 发布id  查询发布表数据
        //将 List<Album>转为List<Publish>
        List<Publish> publishList = new ArrayList<>();
        if(!CollectionUtils.isEmpty(albumList)){
            for (Album album : albumList) {//循环遍历相册表 根据发布id查询动态发布表
                ObjectId publishId = album.getPublishId();//发布id
                Publish publish = mongoTemplate.findById(publishId, Publish.class);
                if(!StringUtils.isEmpty(publish)) {
                    publishList.add(publish);
                }
            }
        }
        //3.封装分页对象返回
        long pages = (counts / pagesize) + (counts%pagesize > 0 ? 1:0);
        return new PageResult<>(counts,(long)pagesize,pages,(long)page,publishList);
    }

    /**
     * 单条动态查询
     */
    @Override
    public Publish querySinglePublish(String publishId) {
        return mongoTemplate.findById(new ObjectId(publishId), Publish.class);
    }

    /**
     * 根据发布id更新动态状态
     * @param publishId
     * @param state
     */
    @Override
    public void updatePublish(String publishId, Integer state) {
        Query query = new Query();
        query.addCriteria(Criteria.where("id").is(new ObjectId(publishId)));
        Update update = new Update();
        update.set("state",state);
        mongoTemplate.updateFirst(query,update,Publish.class);
    }
}

消费者

package com.tanhua.server.controller;

import com.tanhua.domain.vo.*;
import com.tanhua.server.service.MomentService;
import com.tanhua.server.service.TodayBestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;

/**
 * 圈子功能控制层
 */
@RestController
@RequestMapping("/movements")
public class MomentController {


    @Autowired
    private MomentService momentService;

    /**
     * 发布动态
     */
    @RequestMapping(method = RequestMethod.POST)
    public ResponseEntity savePublish(PublishVo publishVo,MultipartFile[] imageContent) {
        momentService.savePublish(imageContent,publishVo);
        return ResponseEntity.ok(null);
    }

    /**
     * 好友动态
     */
    @RequestMapping(method = RequestMethod.GET)
    public ResponseEntity queryPublishByPage(@RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "10") int pagesize) {
        PageResult<MomentVo> pageResult = momentService.queryPublishByPage(page,pagesize);
        return ResponseEntity.ok(pageResult);
    }

    /**
     * 推荐动态(陌生人动态)
     */
    @RequestMapping(value = "/recommend",method = RequestMethod.GET)
    public ResponseEntity queryPublishByRecommendQuanzi(@RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "10") int pagesize) {
        PageResult<MomentVo> pageResult = momentService.queryPublishByRecommendQuanzi(page,pagesize);
        return ResponseEntity.ok(pageResult);
    }

    /**
     * 用户动态
     * 1.首页推荐用户列表-点击用户进入会查询用户动态
     * 2.我的模块-我的动态-查看当前用户动态
     */
    @RequestMapping(value = "/all",method = RequestMethod.GET)
    public ResponseEntity queryPublishByAlbum(@RequestParam(defaultValue = "1") int page,
                                                        @RequestParam(defaultValue = "10") int pagesize,Long userId) {
        PageResult<MomentVo> pageResult = momentService.queryPublishByAlbum(page,pagesize,userId);
        return ResponseEntity.ok(pageResult);
    }


    /**
     * 动态点赞
     * publishId:动态id
     */
    @RequestMapping(value = "/{id}/like",method = RequestMethod.GET)
    public ResponseEntity saveComment(@PathVariable("id") String publishId ) {
        int likeCount = momentService.saveComment(publishId);
        return ResponseEntity.ok(likeCount);
    }


    /**
     * 动态取消点赞
     * publishId:动态id
     */
    @RequestMapping(value = "/{id}/dislike",method = RequestMethod.GET)
    public ResponseEntity removeComment(@PathVariable("id") String publishId ) {
        int likeCount = momentService.removeComment(publishId);
        return ResponseEntity.ok(likeCount);
    }


    /**
     * 动态喜欢
     * publishId:动态id
     */
    @RequestMapping(value = "/{id}/love",method = RequestMethod.GET)
    public ResponseEntity saveCommentLove(@PathVariable("id") String publishId ) {
        int loveCount = momentService.saveCommentLove(publishId);
        return ResponseEntity.ok(loveCount);
    }

    /**
     * 动态取消喜欢
     * publishId:动态id
     */
    @RequestMapping(value = "/{id}/unlove",method = RequestMethod.GET)
    public ResponseEntity saveCommentUnLove(@PathVariable("id") String publishId ) {
        int unLoveCount = momentService.saveCommentUnLove(publishId);
        return ResponseEntity.ok(unLoveCount);
    }


    /**
     * 单条动态查询
     */
    @RequestMapping(value = "/{id}",method = RequestMethod.GET)
    public ResponseEntity querySinglePublish(@PathVariable("id") String publishId) {
        MomentVo momentVo = momentService.querySinglePublish(publishId);
        return ResponseEntity.ok(momentVo);
    }


    /**
     * 谁看过我(查询5条)
     * http://localhost:10880/movements/visitors
     */
    @RequestMapping(value = "/visitors",method = RequestMethod.GET)
    public ResponseEntity queryVisitors() {
        List<VisitorVo> visitorVoList = momentService.queryVisitors();
        return ResponseEntity.ok(visitorVoList);
    }
}
MomentService
package com.tanhua.server.service;

import com.tanhua.commons.exception.TanHuaException;
import com.tanhua.commons.templates.OssTemplate;
import com.tanhua.domain.db.UserInfo;
import com.tanhua.domain.mongo.Comment;
import com.tanhua.domain.mongo.Publish;
import com.tanhua.domain.mongo.Visitor;
import com.tanhua.domain.vo.*;
import com.tanhua.dubbo.api.db.UserInfoApi;
import com.tanhua.dubbo.api.mongo.CommentApi;
import com.tanhua.dubbo.api.mongo.PublishApi;
import com.tanhua.dubbo.api.mongo.VisitorsApi;
import com.tanhua.server.interceptor.UserHolder;
import com.tanhua.server.utils.RelativeDateFormat;
import org.apache.dubbo.config.annotation.Reference;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.bson.types.ObjectId;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 圈子业务逻辑处理层
 */
@Service
public class MomentService {

    @Reference
    private PublishApi publishApi;

    @Reference
    private UserInfoApi userInfoApi;

    @Reference
    private VisitorsApi visitorsApi;


    @Autowired
    private OssTemplate ossTemplate;

    @Reference
    private CommentApi commentApi;

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    /**
     * 发布动态
     */
    public void savePublish(MultipartFile[] imageContent, PublishVo publishVo) {
        try {
            Long userId = UserHolder.getUserId();
            //1.调用Oss组件上图图片
            List<String> medias = new ArrayList<>();
            //1.如果不为空
            if(imageContent != null) {
                //将图片遍历存入,阿里云图片
                for (MultipartFile file : imageContent) {
                    String imageUrl = ossTemplate.upload(file.getOriginalFilename(), file.getInputStream());
                    medias.add(imageUrl);
                }
                publishVo.setMedias(medias);//图片urls
            }
            publishVo.setUserId(userId);//当前发布动态用户id
            //发布动态返回一个publishId
            String publishId = publishApi.savePublish(publishVo);
            //发送消息到中间件
            rocketMQTemplate.convertAndSend("tanhua-publish",publishId);
        } catch (IOException e) {
            throw new TanHuaException(ErrorResult.error());
        }
    }

    /**
     * 好友动态
     */
    public PageResult<MomentVo> queryPublishByPage(int page, int pagesize) {
        PageResult<MomentVo> voPageResult = new PageResult<>();

        Long userId = UserHolder.getUserId();
        //a.调用服务:分页查询发布表数据(分别查询好友时间线表 发布表)
        PageResult<Publish> publishPageResult = publishApi.queryPublishByPage(page,pagesize,userId);
        if(publishPageResult == null || CollectionUtils.isEmpty(publishPageResult.getItems())){
            return new PageResult<>(0l,10l,0l,0l,null);
        }
        //b.调用服务:根据发布用户id 查询用户信息
        List<Publish> publishList = publishPageResult.getItems();

        //将List<Publish>转为List<MomentVo>
        List<MomentVo>  momentVoList = new ArrayList<>();//返回给app集合
        for (Publish publish : publishList) {
            MomentVo momentVo = new MomentVo();
            Long publishUserId = publish.getUserId();//发布动态用户id
            UserInfo userInfo = userInfoApi.queryUserInfo(publishUserId);
            BeanUtils.copyProperties(userInfo,momentVo);//头像 昵称 性别 年龄
            BeanUtils.copyProperties(publish,momentVo);//文字动态、 点赞 评论 喜欢数


            momentVo.setImageContent(publish.getMedias().toArray(new String[]{}));//图片urls地址
            momentVo.setDistance("1米");//距离1米
            momentVo.setCreateDate(RelativeDateFormat.format(new Date(publish.getCreated())));//发布动态时间
            if(!StringUtils.isEmpty(userInfo.getTags())){
                momentVo.setTags(userInfo.getTags().split(","));
            }
            momentVo.setId(publish.getId().toHexString());//动态id
            momentVo.setUserId(userInfo.getId());//当前发布动态用户id

            String key = "comment_"+userId+"_"+publish.getId().toHexString();
            if(StringUtils.isEmpty(redisTemplate.opsForValue().get(key))){
                momentVo.setHasLiked(0);是否点赞(1是,0否)
            }else
            {
                momentVo.setHasLiked(1);是否点赞(1是,0否)
            }
            momentVoList.add(momentVo);
        }
        //c.将以上发布数据与用户信息封装Vo返回
        BeanUtils.copyProperties(publishPageResult,voPageResult);
        voPageResult.setItems(momentVoList);
        return voPageResult;
    }

    /**
     * 推荐动态(陌生人动态)
     */
    public PageResult<MomentVo> queryPublishByRecommendQuanzi(int page, int pagesize) {

        PageResult<MomentVo> voPageResult = new PageResult<>();

        Long userId = UserHolder.getUserId();
        //a.调用服务:分页查询发布表数据(分别查询好友时间线表 发布表)
        PageResult<Publish> publishPageResult = publishApi.queryPublishByRecommendQuanzi(page,pagesize,userId);
        if(publishPageResult == null || CollectionUtils.isEmpty(publishPageResult.getItems())){
            return new PageResult<>(0l,10l,0l,0l,null);
        }
        //b.调用服务:根据发布用户id 查询用户信息
        List<Publish> publishList = publishPageResult.getItems();

        //将List<Publish>转为List<MomentVo>
        List<MomentVo>  momentVoList = new ArrayList<>();//返回给app集合
        for (Publish publish : publishList) {
            MomentVo momentVo = new MomentVo();
            Long publishUserId = publish.getUserId();//发布动态用户id
            UserInfo userInfo = userInfoApi.queryUserInfo(publishUserId);
            BeanUtils.copyProperties(userInfo,momentVo);//头像 昵称 性别 年龄
            BeanUtils.copyProperties(publish,momentVo);//文字动态、 点赞 评论 喜欢数


            momentVo.setImageContent(publish.getMedias().toArray(new String[]{}));//图片urls地址
            momentVo.setDistance("1米");//距离1米
            momentVo.setCreateDate(RelativeDateFormat.format(new Date(publish.getCreated())));//发布动态时间
            if(!StringUtils.isEmpty(userInfo.getTags())){
                momentVo.setTags(userInfo.getTags().split(","));
            }
            momentVo.setId(publish.getId().toHexString());//动态id
            momentVo.setUserId(userInfo.getId());//当前发布动态用户id
            String key2 = "love_"+userId+"_"+publish.getId().toHexString();
            if(StringUtils.isEmpty(redisTemplate.opsForValue().get(key2))) {
                momentVo.setHasLoved(0);是否喜欢(1是,0否)
            }else
            {
                momentVo.setHasLoved(1);是否喜欢(1是,0否)
            }
            String key = "comment_"+userId+"_"+publish.getId().toHexString();
            if(StringUtils.isEmpty(redisTemplate.opsForValue().get(key))){
                momentVo.setHasLiked(0);是否点赞(1是,0否)
            }else
            {
                momentVo.setHasLiked(1);是否点赞(1是,0否)
            }

            momentVoList.add(momentVo);
        }
        //c.将以上发布数据与用户信息封装Vo返回
        BeanUtils.copyProperties(publishPageResult,voPageResult);
        voPageResult.setItems(momentVoList);
        return voPageResult;
    }

    /**
     * 用户动态
     * 1.首页推荐用户列表-点击用户进入会查询用户动态
     * 2.我的模块-我的动态-查看当前用户动态
     */
    public PageResult<MomentVo> queryPublishByAlbum(int page, int pagesize, Long userId) {
        PageResult<MomentVo> voPageResult = new PageResult<>();
        //a.调用服务:分页查询发布表数据(分别查询好友时间线表 发布表)
        PageResult<Publish> publishPageResult = publishApi.queryPublishByAlbum(page,pagesize,userId);
        if(publishPageResult == null || CollectionUtils.isEmpty(publishPageResult.getItems())){
            return new PageResult<>(0l,10l,0l,0l,null);
        }
        //b.调用服务:根据发布用户id 查询用户信息
        List<Publish> publishList = publishPageResult.getItems();

        //将List<Publish>转为List<MomentVo>
        List<MomentVo>  momentVoList = new ArrayList<>();//返回给app集合
        for (Publish publish : publishList) {
            MomentVo momentVo = new MomentVo();
            Long publishUserId = publish.getUserId();//发布动态用户id
            UserInfo userInfo = userInfoApi.queryUserInfo(publishUserId);
            BeanUtils.copyProperties(userInfo,momentVo);//头像 昵称 性别 年龄
            BeanUtils.copyProperties(publish,momentVo);//文字动态、 点赞 评论 喜欢数


            momentVo.setImageContent(publish.getMedias().toArray(new String[]{}));//图片urls地址
            momentVo.setDistance("1米");//距离1米
            momentVo.setCreateDate(RelativeDateFormat.format(new Date(publish.getCreated())));//发布动态时间
            if(!StringUtils.isEmpty(userInfo.getTags())){
                momentVo.setTags(userInfo.getTags().split(","));
            }
            momentVo.setId(publish.getId().toHexString());//动态id
            momentVo.setUserId(userInfo.getId());//当前发布动态用户id

            String key = "comment_"+userId+"_"+publish.getId().toHexString();
            if(StringUtils.isEmpty(redisTemplate.opsForValue().get(key))){
                momentVo.setHasLiked(0);是否点赞(1是,0否)
            }else
            {
                momentVo.setHasLiked(1);是否点赞(1是,0否)
            }

            momentVoList.add(momentVo);
        }
        //c.将以上发布数据与用户信息封装Vo返回
        BeanUtils.copyProperties(publishPageResult,voPageResult);
        voPageResult.setItems(momentVoList);
        return voPageResult;
    }

    /**
     * 动态点赞
     * publishId:动态id
     */
    public int saveComment(String publishId) {
        Long userId = UserHolder.getUserId();//当前用户id
        //a.服务提供者:点赞服务方法 返回点赞数量
        Comment comment = new Comment();
        comment.setPublishId(new ObjectId(publishId));//将String类型的发布id 转为ObjectId类型
        comment.setCommentType(1);//评论类型,1-点赞,2-评论,3-喜欢
        comment.setPubType(1);//评论内容类型: 1-对动态操作 2-对视频操作 3-对评论操作
        comment.setUserId(userId);//当前点赞用户id
        if(comment.getPubType() == 1){
            //根据发布id查询发布表 获取userId(PublishUserId)
            Publish publish = publishApi.querySinglePublish(publishId);
            comment.setPublishUserId(publish.getUserId());//被评论的用户id
        }
        int likeCount = commentApi.saveComment(comment);
        //b.将点赞记录写入redis
        String key = "comment_"+userId+"_"+publishId;//保证唯一 业务前缀标识+userId+publishId
        redisTemplate.opsForValue().set(key,"1");
        //c.将点赞数封装返回给app展示
        return likeCount;
    }

    /**
     * 动态取消点赞
     * publishId:动态id
     */
    public int removeComment(String publishId) {
        Long userId = UserHolder.getUserId();
        //a.服务提供者:取消点赞服务方法 返回点赞数量
        Comment comment = new Comment();
        comment.setPublishId(new ObjectId(publishId));//动态id
        comment.setCommentType(1);//评论类型,1-点赞,2-评论,3-喜欢
        comment.setPubType(1);//评论内容类型: 1-对动态操作 2-对视频操作 3-对评论操作
        comment.setUserId(userId);//userId
        int likeCount = commentApi.removeComment(comment);
        //b.将点赞记录从redis删除
        String key = "comment_"+userId+"_"+publishId;//保证唯一 业务前缀标识+userId+publishId
        redisTemplate.delete(key);
        //c.将点赞数封装返回给app展示
        return likeCount;
    }

    /**
     * 动态喜欢
     * publishId:动态id
     */
    public int saveCommentLove(String publishId) {
        Long userId = UserHolder.getUserId();//当前用户id
        //a.服务提供者:点赞服务方法 返回点赞数量
        Comment comment = new Comment();
        comment.setPublishId(new ObjectId(publishId));//将String类型的发布id 转为ObjectId类型
        comment.setCommentType(3);//评论类型,1-点赞,2-评论,3-喜欢
        comment.setPubType(1);//评论内容类型: 1-对动态操作 2-对视频操作 3-对评论操作
        comment.setUserId(userId);//当前点赞用户id
        if(comment.getPubType() == 1){
            //根据发布id查询发布表 获取userId(PublishUserId)
            Publish publish = publishApi.querySinglePublish(publishId);
            comment.setPublishUserId(publish.getUserId());//被评论的用户id
        }
        int likeCount = commentApi.saveComment(comment);
        //b.将点赞记录写入redis
        String key = "love_"+userId+"_"+publishId;//保证唯一 业务前缀标识+userId+publishId
        redisTemplate.opsForValue().set(key,"1");
        //c.将点赞数封装返回给app展示
        return likeCount;
    }

    /**
     * 动态取消喜欢
     * publishId:动态id
     */
    public int saveCommentUnLove(String publishId) {
        Long userId = UserHolder.getUserId();
        //a.服务提供者:取消点赞服务方法 返回点赞数量
        Comment comment = new Comment();
        comment.setPublishId(new ObjectId(publishId));//动态id
        comment.setCommentType(3);//评论类型,1-点赞,2-评论,3-喜欢
        comment.setPubType(1);//评论内容类型: 1-对动态操作 2-对视频操作 3-对评论操作
        comment.setUserId(userId);//userId
        int likeCount = commentApi.removeComment(comment);
        //b.将点赞记录从redis删除
        String key = "love_"+userId+"_"+publishId;//保证唯一 业务前缀标识+userId+publishId
        redisTemplate.delete(key);
        //c.将点赞数封装返回给app展示
        return likeCount;
    }

    /**
     * 单条动态查询
     */
    public MomentVo querySinglePublish(String publishId) {
        //a.调用服务:根据发布id查询动态发布表
        Publish publish = publishApi.querySinglePublish(publishId);
        if(publish == null){
            return null;
        }
        //b.调用服务:根据发布用户id 查询用户信息
        //将List<Publish>转为List<MomentVo>
        MomentVo momentVo = new MomentVo();
        Long publishUserId = publish.getUserId();//发布动态用户id
        UserInfo userInfo = userInfoApi.queryUserInfo(publishUserId);
        BeanUtils.copyProperties(userInfo,momentVo);//头像 昵称 性别 年龄
        BeanUtils.copyProperties(publish,momentVo);//文字动态、 点赞 评论 喜欢数


        momentVo.setImageContent(publish.getMedias().toArray(new String[]{}));//图片urls地址
        momentVo.setDistance("1米");//距离1米
        momentVo.setCreateDate(RelativeDateFormat.format(new Date(publish.getCreated())));//发布动态时间
        if(!StringUtils.isEmpty(userInfo.getTags())){
            momentVo.setTags(userInfo.getTags().split(","));
        }
        momentVo.setId(publish.getId().toHexString());//动态id
        momentVo.setUserId(userInfo.getId());//当前发布动态用户id
        return momentVo;
    }

    /**
     * 谁看过我(查询5条)
     */
    public List<VisitorVo> queryVisitors() {
        List<VisitorVo> list = new ArrayList<>();
        Long userId = UserHolder.getUserId();//当前用户登录id
        //a.查询redis上次登录时间是否存在
        String key="visitors_time_"+userId;
        String lastLoginTime = redisTemplate.opsForValue().get(key);//key:visitors_time_1 value:上次登录的时间

        ///b.如果上次登录时间存在,date(访客访问时间)>上次登录时间5条记录
        List<Visitor> visitorList = new ArrayList<>();
        if(!StringUtils.isEmpty(lastLoginTime)){
            //将String lastLoginTime转为Long lastLoginTime
            long lastLoginTimeLong = Long.parseLong(lastLoginTime);
            visitorList = visitorsApi.queryVisitorsByDate(lastLoginTimeLong,userId);
        }else {
            //c.如果上次登录时间不存在,从所有访客记录中查询5条记录
            visitorList = visitorsApi.queryVisitors(userId);
        }
        for (Visitor visitor : visitorList) {
            VisitorVo visitorVo = new VisitorVo();
            UserInfo userInfo = userInfoApi.queryUserInfo(visitor.getVisitorUserId());
            BeanUtils.copyProperties(userInfo,visitorVo);//头像 昵称 性别 年龄
            visitorVo.setTags(userInfo.getTags().split(","));//标签
            visitorVo.setId(visitor.getVisitorUserId());//访客用户id
            visitorVo.setFateValue(visitor.getScore().intValue());//访客表中缘分值
            list.add(visitorVo);
        }
        //d.将当前登录时间记录redis
        redisTemplate.opsForValue().set(key,System.currentTimeMillis()+"");
        return list;

    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
继“Java开发微信朋友圈PC版系统-架构1.0”之后,debug这段时间日撸夜撸,终于赶在春节放假前给诸位带来了这一系统的架构2.0版本,特此分享给诸位进行学习,以掌握、巩固更多的技术栈以及项目和产品开发经验,同时也为即将到来的金三银四跳槽季做准备! 言归正传,下面仍然以问答的方式介绍下本门课程的相关内容! (1)问题一:这是一门什么样的课程? 很明显,本门课程是建立在架构1.0,即 第1门课程 的基础发布的,包含了架构1.0的内容,即它仍然是一门项目、产品实战课,基于Spring Boot2.X + 分布式中间件开发的一款类似“新浪微博”、“QQ空间”、“微信朋友圈”PC版的互联网社交软件,包含完整的门户网前端 以及 后台系统管理端,可以说是一套相当完整的系统! (2)问题二:架构2.0融入了哪些新技术以及各自有什么作用? 本课程对应着系统架构2.0,即第2阶段,主要目标:基于架构1.0,优化系统的整体性能,实现一个真正的互联网社交产品;其中,可以学习到的技术干货非常多,包括:系统架构设计、Spring Boot2.X、缓存Redis、多线程并发编程、消息中间件RabbitMQ、全文搜索引擎Elastic Search、前后端消息实时通知WebSocket、分布式任务调度中间件Elastic Job、Http Restful编程、Http通信OKHttp3、分布式全局唯一ID、雪花算法SnowFlake、注册中心ZooKeeper、Shiro+Redis 集群Session共享、敏感词自动过滤、Java8 等等; A.  基于Elastic Search实现首页列表数据的初始化加载、首页全文检索;B.  基于缓存Redis缓存首页朋友圈“是否已点赞、收藏、关注、评论、转发”等统计数据;整合Shiro实现集群部署模式下Session共享;C.  多线程并发编程并发处理系统产生的废弃图片、文件数据;D.  基于Elastic Job切片作业调度分布式多线程清理系统产生的废弃图片;E.  基于RabbitMQ解耦同步调用的服务模块,实现服务模块之间异步通信;F.  基于WebSocket实现系统后端 与 首页前端 当前登录用户实时消息通知;G.  基于OKHttp3、Restful风格的Rest API实现ES文档、分词数据存储与检索;H.  分布式全局唯一ID 雪花算法SnowFlake实现朋友圈图片的唯一命名;I.  ZooKeeper充当Elastic Job创建的系统作业的注册中心;J.  为塑造一个健康的网络环境,对用户发的朋友圈、评论、回复内容进行敏感词过滤;K.  大量优雅的Java8  Lambda编程、Stream编程;  (3)问题三:系统运行起来有效果图看吗?

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值