java 探花交友项目day5 推荐好友列表 MongoDB集群 发布动态,查询动态 圈子功能
创始人
2024-05-20 14:42:51

推荐好友列表

需求分析

推荐好友:分页形式查询推荐的用户列表,根据评分排序显示

 代码实现:

tanhuaController:

/**
 * 查询分页推荐好友列表
 */
@GetMapping("/recommendation")
public ResponseEntity recommendation(RecommendUserDto dto) {
    PageResult pr = tanhuaService.recommendation(dto);
    return ResponseEntity.ok(pr);
}

tanhuaService:

public PageResult recommendation(RecommendUserDto dto) {//1、获取用户idLong userId = UserHolder.getUserId();//2、调用recommendUserApi分页查询数据列表(PageResult -- RecommendUser)PageResult pr = recommendUserApi.queryRecommendUserList(dto.getPage(),dto.getPagesize(),userId);//3、获取分页中的RecommendUser数据列表List items = (List) pr.getItems();//4、判断列表是否为空if(items == null) {return pr;}//5、提取所有推荐的用户id列表List ids = CollUtil.getFieldValues(items, "userId", Long.class);UserInfo userInfo = new UserInfo();userInfo.setAge(dto.getAge());userInfo.setGender(dto.getGender());//6、构建查询条件,批量查询所有的用户详情Map map = userInfoApi.findByIds(ids, userInfo);//7、循环推荐的数据列表,构建vo对象Long count=0L;List list = new ArrayList<>();for (RecommendUser item : items) {UserInfo info = map.get(item.getUserId());if(info!=null) {TodayBest vo = TodayBest.init(info, item);list.add(vo);count++;}}pr.setCounts(count);//8、构造返回值pr.setItems(list);log.info("最终推荐表:{}",pr);return pr;
}

RecommendUserApiImpl

//分页查询
public PageResult queryRecommendUserList(Integer page, Integer pagesize, Long toUserId) {
    //1、构建Criteria对象
    Criteria criteria = Criteria.where("toUserId").is(toUserId);
    //2、创建Query对象
    Query query = Query.query(criteria).with(Sort.by(Sort.Order.desc("score"))).limit(pagesize)
            .skip((page - 1) * pagesize);
    //3、调用mongoTemplate查询
    List list = mongoTemplate.find(query, RecommendUser.class);
    long count = mongoTemplate.count(query, RecommendUser.class);
    //4、构建返回值PageResult
    return  new PageResult(page,pagesize,count,list);
}

请求dto对象

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

@Data
@NoArgsConstructor
@AllArgsConstructor
public class RecommendUserDto {

    private Integer page = 1; //当前页数
    private Integer pagesize = 10; //页尺寸
    private String gender; //性别 man woman
    private String lastLogin; //近期登陆时间
    private Integer age; //年龄
    private String city; //居住地
    private String education; //学历
}

我们定义了一个userInfoApi的查询多个userinfo的功能,这样在查询出推荐用户表的时候,我们根据多个id在userInfoApi直接一次性查询到,而且可以增加年龄性别等条件,这样查询出再在controller层进行筛选

MongoDB集群

问题分析: 

集群概述:

 

 副本集群:多个服务器存储相同的数据保证可靠性。但是不能解决海量数据问题

分片集群

 

 为了保证每个服务的高可用,需要服务配置副本集群,这里仅以单节点为例

路由服务如何获取文档的分片服务器位置呢?

分片集群:分片策略

圈子功能

需求分析

探花交友项目中的圈子功能,类似微信的朋友圈

发布动态

浏览好友、个人、推荐动态

动态互动如:点赞、评论、喜欢等

 

表结构设计

优点 :开发难度较小 易于理解

缺点 :动态对特定好友可见/不可见,实现难度较大 效率较低 

优点 开发难度较小 可以完成所有业务功能

缺点 效率较低 索引空间占用 

 也就是,我们另外创建个时间线表,来存放动态表的id ,用户 及其好友

 

 

环境搭建

Mongodb中实现字段的自增:两种解决方法(1、使用redis保证自动增长,2、使用mongodb自定义表)  redis在用户端,我们使用第二种

mongo主键自增

1.创建实体类

@Document(collection = "sequence")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Sequence {

    private ObjectId id;

    private long seqId; //自增序列

    private String collName;  //集合名称
}

2.编写service

@Component
public class IdWorker {

    @Autowired
    private MongoTemplate mongoTemplate;

    public Long getNextId(String collName) {
        Query query = new Query(Criteria.where("collName").is(collName));

        Update update = new Update();
        update.inc("seqId", 1);

        FindAndModifyOptions options = new FindAndModifyOptions();
        options.upsert(true);
        options.returnNew(true);

        Sequence sequence = mongoTemplate.findAndModify(query, update, options, Sequence.class);
        return sequence.getSeqId();
    }
}

实体类

Movement:发布信息表(总记录表数据)

@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "movement")
public class Movement implements java.io.Serializable {private ObjectId id; //主键idprivate Long pid; //Long类型,用于推荐系统的模型(自动增长)private Long created; //发布时间private Long userId;private String textContent; //文字private List medias; //媒体数据,图片或小视频 urlprivate String longitude; //经度private String latitude; //纬度private String locationName; //位置名称private Integer state = 0;//状态 0:未审(默认),1:通过,2:驳回
}

MovementTimeLine:好友时间线表,用于存储好友发布(或推荐)的数据,每一个用户一张表进行存储


/**
 * 好友时间线表,用于存储好友发布的数据
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "movement_timeLine")
public class MovementTimeLine implements java.io.Serializable {

    private static final long serialVersionUID = 9096178416317502524L;
    private ObjectId id;
    private ObjectId movementId;//动态id
    private Long userId;   //发布动态用户id
    private Long friendId; // 可见好友id
    private Long created; //发布的时间
}
 

Friend 好友关系表


/**
 * 好友表:好友关系表
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "friend")
public class Friend implements java.io.Serializable{

    private static final long serialVersionUID = 6003135946820874230L;
    private ObjectId id;
    private Long userId; //用户id
    private Long friendId; //好友id
    private Long created; //时间

}
 

MovementApiImpl:

@DubboService
public class MovementApiImpl implements MovementApi{@Autowiredprivate MongoTemplate mongoTemplate;@Autowiredprivate IdWorker idWorker;@Overridepublic void publish(Movement movement) {//1.保存动态详情//设置PID和发布时间movement.setPid(idWorker.getNextId("movement"));movement.setCreated(System.currentTimeMillis());//id会自动生成 我们定义的时候用了ObjectIdmongoTemplate.save(movement);//2.查询当前用户的好友数据Criteria criteria=Criteria.where("userId").is(movement.getUserId());Query query=Query.query(criteria);List friends = mongoTemplate.find(query, Friend.class);log.info("friends:{}",friends);//3.循环好友数据,构建时间线数据存入数据库for (Friend friend : friends) {MovementTimeLine timeLine =new MovementTimeLine();timeLine.setMovementId(movement.getId());timeLine.setUserId(friend.getUserId());timeLine.setFriendId(friend.getFriendId());timeLine.setCreated(System.currentTimeMillis());log.info("timeline:{}",timeLine);mongoTemplate.save(timeLine);}}
}

MovementController

@RestController
@RequestMapping("/movements")
public class MovementController {@Autowiredprivate MovementService movementService;/*** 发布动态*/@PostMappingpublic ResponseEntity movements(Movement movement,MultipartFile imageContent[]) throws IOException {movementService.publishMovement(movement,imageContent);return ResponseEntity.ok(null);}
}

MovementService

@Service
public class MovementService {@Autowiredprivate OssTemplate ossTemplate;@DubboReferenceprivate MovementApi movementApi;/*** 发布动态* @param movement* @param imageContent*/public void publishMovement(Movement movement, MultipartFile[] imageContent) throws IOException {//1.判断发布动态内容是否存在if (StringUtils.isEmpty(movement.getTextContent())){throw new BusinessException(ErrorResult.contentError());}//2.获取当前登录用户idLong userId = UserHolder.getUserId();//3.将文件内容上传到阿里云,获取请求地址List medias=new ArrayList<>();for (MultipartFile multipartFile : imageContent) {String upload =ossTemplate.upload(multipartFile.getOriginalFilename(),multipartFile.getInputStream());medias.add(upload);}//4.将数据封装到movement对象movement.setUserId(userId);movement.setMedias(medias);//5.调用API完成发布动态movementApi.publish(movement);}
}

大量的时间线数据同步写入的问题如何解决?

我们在保存时间线数据的时候,用的循环保存,如果好友量特别大,那么进程就会特别慢

发布动态-异步处理

@Async: Spring提供的异步处理注解,被此注解标注的方法会在新的线程中执行,其实就相当于我们自己new Thread。

解决:

我们将需要新开线程的内容提出成一个类:

@Component
public class TimeLineService {

    @Autowired
    private MongoTemplate mongoTemplate;

    @Async
    public void saveTimeLine(Long userId, ObjectId movementId) {
        //2、查询当前用户的好友数据
        Criteria criteria = Criteria.where("userId").is(userId);
        Query query = Query.query(criteria);
        List friends = mongoTemplate.find(query, Friend.class);
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //3、循环好友数据,构建时间线数据存入数据库
        for (Friend friend : friends) {
            MovementTimeLine timeLine = new MovementTimeLine();
            timeLine.setMovementId(movementId);
            timeLine.setUserId(friend.getUserId());
            timeLine.setFriendId(friend.getFriendId());
            timeLine.setCreated(System.currentTimeMillis());
            mongoTemplate.save(timeLine);
        }
    }
}

我的动态

 

相关内容

热门资讯

世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...