1. 圈子中的互动功能的分析与设计

1.1 数据库的原则

经过我们分析,圈子中的互动数据有一下特点:

  • 数据量非常大
  • 数据变化迅速
  • 数据价值相对较低

综上,我们采用MongoDB来存储圈子中的互动数据

1.2 表设计

我们采用一张表来记录所有的互动信息,通过指定不同互动类型的type来区分是点赞还是评论或者时喜欢。表结构如下:

对应的实体类如下:

@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "comment")
public class Comment implements java.io.Serializable{private ObjectId id;private ObjectId publishId;    //发布idprivate Integer commentType;   //评论类型,1-点赞,2-评论,3-喜欢private String content;        //评论内容  private Long userId;           //评论人   private Long publishUserId;    //被评论人IDprivate Long created;          //发表时间private Integer likeCount = 0; //当前评论的点赞数}

1.3 Redis缓存

我们在刷动态的时候,每一次刷新除了需要查询动态信息外,还需要查询当前用户是否对这条动态点过赞,因此查询的内容过多导致效率低。因此,我们引入Redis,在里面保存用户点赞的信息,来判断用户是否点过赞或者喜欢过。

Redis中Key说明:
Redis中采用Hash类型存储,一条动态对应一个key,value里面又包含hashKey和value,其中一个用户点赞对应hashKey,value为标志位,1代表用户点赞。

  • key:MOVEMENTS_INTERACT_KEY + movementId;
  • hashKey:MOVEMENT_LIKE_HASHKEY + userId;
  • value:MOVEMENT_LOVE_HASHKEY + userId;

这里判断用户是否已经点过赞了,可以从MongoDB中获取,也可以查询Redis。本项目中从MongoDB中获取。

2. 用户点赞和取消点赞

2.1 需求分析

用户点赞的业务流程如下:

  • 根据前端传来的动态ID,先去MongoDB中的评论表中查询是否有记录。如果有了在抛出异常
  • 封装comment对象,调用api保存
  • Comment保存成功后,还需要同步更新动态表格中的点赞数量+1
  • 在Redis中写入数据,保存用户点赞信息。

用户取消点赞:和用户点赞正好相反

  • 根据前端传来的动态ID,先去MongoDB中的评论表中查询是否有记录。如果没有则抛出异常
  • 封装comment对象,调用api保存
  • Comment保存成功后,还需要同步更新动态表格中的点赞数量-1
  • 在Redis中删除用户点赞的信息


接口如下:

2.2 代码实现

Controller

/*** 用户点赞* @param movementId 动态Id* @return 点赞之后最新的点赞数量*/
@GetMapping("/{id}/likeMovement")
public ResponseEntity like(@PathVariable(name = "id") String movementId) {// 1. 调用Service方法完成点赞Integer likeCount = this.commentService.like(movementId);// 2. 返回结果return ResponseEntity.ok(likeCount);
}/*** 取消点赞* @param movementId 动态Id* @return 取消点赞之后最新的点赞数量*/
@GetMapping("/dislikeMovement/{id}")
public ResponseEntity dislike(@PathVariable(name = "id") String movementId) {// 1. 调用Service方法完成点赞Integer likeCount = this.commentService.dislike(movementId);// 2. 返回结果return ResponseEntity.ok(likeCount);
}

Service

public Integer like(String movementId) {// 1. 获取当前用户IDLong userId = UserHolder.getUserId();// 2. 查询comment表,判断用户是否已经点过赞,如果点过,则抛出异常Boolean isLiked = this.commentApi.check(movementId, CommentType.LIKE.getType(), userId);if (isLiked) {// 用户已经点过赞了,这里就直接抛出异常throw new BusinessException(ErrorResult.likeError());}// 3. 封装Comment对象,调用api保存commentComment comment = new Comment();comment.setPublishId(new ObjectId(movementId));comment.setUserId(userId);comment.setCreated(System.currentTimeMillis());Integer count = this.commentApi.save(comment, CommentType.LIKE.getType());// 4. 将用户点赞保存到Redis// 构造key prefix + movementIdString key = MOVEMENTS_INTERACT_KEY + movementId;// 构造哈希key prefix + userIdString hashKey = MOVEMENT_LIKE_HASHKEY + userId;this.redisTemplate.opsForHash().put(key, hashKey, "1");// 5. 返回结果return count;
}// 取消点赞
public Integer dislike(String movementId) {// 1. 获取当前用户idLong userId = UserHolder.getUserId();// 2. 查询用户是否点过赞,如果没有点过赞,则抛出异常Boolean isLiked = this.commentApi.check(movementId, CommentType.LIKE.getType(), userId);if (!isLiked) {// 用户已经点过赞了,这里就直接抛出异常throw new BusinessException(ErrorResult.disLikeError());}// 3. 调用api取消点赞Comment comment = new Comment();comment.setPublishId(new ObjectId(movementId));comment.setUserId(userId);Integer count = this.commentApi.delete(comment, CommentType.LIKE.getType());// 4. 删除redis中的键// 构造key prefix + movementIdString key = MOVEMENTS_INTERACT_KEY + movementId;// 构造哈希key prefix + userIdString hashKey = MOVEMENT_LIKE_HASHKEY + userId;this.redisTemplate.opsForHash().delete(key, hashKey);// 5. 返回结果return count;
}

API

@Override
public Boolean check(String movementId, int type, Long userId) {// 构建条件 动态id,用户id,点赞类型Criteria criteria = Criteria.where("publishId").is(new ObjectId(movementId)).and("userId").is(userId).and("commentType").is(type);Query query = new Query(criteria);return this.mongoTemplate.exists(query, Comment.class);
}
/**
* 保存Comment到数据库
*
* @param comment 评论对象
* @param type    评论类型
* @return
*/
@Override
public Integer save(Comment comment, int type) {// 1. 从Comment对象中获取到动态id,查询动态id,try {ObjectId publishId = comment.getPublishId();Movement movementById = this.mongoTemplate.findById(publishId, Movement.class);if (movementById != null || type == CommentType.LIKECOMMENT.getType()) {if (movementById == null) {// 1. 获取到动态id的作者 并设置到Comment的publishUserId字段中Comment publishUser = this.mongoTemplate.findById(comment.getPublishId(), Comment.class);if (publishUser != null) {comment.setPublishUserId(publishUser.getPublishUserId());} else {return 0;}} else {comment.setPublishUserId(movementById.getUserId());}// 2. 设置评论的类型comment.setCommentType(type);// 3. 保存到数据库this.mongoTemplate.save(comment);// 4. 根据不同的评论类型,更新Movement或者Comment表中数据表中对象的记录// 4.1 构造查询条件 如果是动态的点赞或者喜欢if (type == CommentType.LIKECOMMENT.getType()) {// 评论点赞Query query = new Query(Criteria.where("id").is(comment.getPublishId()));Update update = new Update();update.inc("likeCount", 1);FindAndModifyOptions findAndModifyOptions = FindAndModifyOptions.options().returnNew(true);return this.mongoTemplate.findAndModify(query, update, findAndModifyOptions, Comment.class).getLikeCount();} else {Criteria criteria = Criteria.where("id").is(movementById.getId());Query query = new Query(criteria);Update update = new Update();Integer commentType = comment.getCommentType();if (commentType == CommentType.LIKE.getType()) {update.inc("likeCount", 1);} else if (commentType == CommentType.COMMENT.getType()) {update.inc("commentCount", 1);} else {update.inc("loveCount", 1);}FindAndModifyOptions findAndModifyOptions = FindAndModifyOptions.options().returnNew(true);// 调用template更新 返回更新后的Movement对象Movement modify = this.mongoTemplate.findAndModify(query, update, findAndModifyOptions, Movement.class);// 根据不同的评论类型,返回对应的计数return modify.getCount(commentType);}} else {return 0;}} catch (Exception e) {throw new RuntimeException(e);}
}
/**
* 取消点赞 喜欢 评论点赞
*
* @param comment 要删除的评论,喜欢 评论点赞
* @return 取消点赞之后最新的点赞数量
*/
@Override
public Integer delete(Comment comment, int type) {// 1. 解析数据ObjectId publishId = comment.getPublishId();Long userId = comment.getUserId();// 2. 构造条件Criteria criteria = Criteria.where("publishId").is(publishId).and("userId").is(userId).and("commentType").is(type);Query query = new Query(criteria);// 3. 删除comment中数据this.mongoTemplate.remove(query, Comment.class);if (type == CommentType.LIKECOMMENT.getType()) {Query modifyQuery = new Query(Criteria.where("id").is(publishId));Update update = new Update();update.inc("likeCount", -1);FindAndModifyOptions findAndModifyOptions = FindAndModifyOptions.options().returnNew(true);return this.mongoTemplate.findAndModify(modifyQuery, update, findAndModifyOptions, Comment.class).getLikeCount();} else {// 4. 更新相应的movement中数据Query modifyQuery = new Query(Criteria.where("id").is(publishId));Update update = new Update();Integer commentType = type;if (commentType == CommentType.LIKE.getType()) {update.inc("likeCount", -1);} else if (commentType == CommentType.COMMENT.getType()) {update.inc("commentCount", -1);} else {update.inc("loveCount", -1);}// 调用template更新 返回更新后的Movement对象FindAndModifyOptions findAndModifyOptions = FindAndModifyOptions.options().returnNew(true);Movement modify = this.mongoTemplate.findAndModify(modifyQuery, update, findAndModifyOptions, Movement.class);// 5.返回结果return modify.getCount(type);}
}

需要注意的是,当我们查询动态的时候,还需要从Redis中查询登录用户是否对这条动态点过赞,如果点过赞,则需要设置相关属性。因此我们修改查询动态列表中的相关代码

if (userInfo != null) {MovementsVo init = MovementsVo.init(userInfo, movement);// 使用EmptyList 包报错 https://blog.csdn.net/fengbin111/article/details/105909654/// 从Redis中获取数据,判断用户书否喜欢过或者点赞过这条动态String key = MOVEMENTS_INTERACT_KEY + movement.getId().toHexString();String loveHashKey = MOVEMENT_LOVE_HASHKEY + UserHolder.getUserId();String likeHashKey = MOVEMENT_LIKE_HASHKEY + UserHolder.getUserId();if (this.redisTemplate.opsForHash().hasKey(key, loveHashKey)) {init.setHasLoved(1);}if (this.redisTemplate.opsForHash().hasKey(key, likeHashKey)) {init.setHasLiked(1);}voList.add(init);
}

3. 用户喜欢和取消喜欢

这里的逻辑和点赞是一致的,就不在赘述。

4. 用户评论

4.1 发布评论

和点赞的逻辑基本一致。用户发送评论,后端将评论保存到comment表中,并更新评论对应的动态的相关计数。接口如下:

代码实现
Controller

/*** 发布一条评论* @param map 动态ID+评论正文* @return*/
@PostMapping
public ResponseEntity publishComment(@RequestBody Map map) {// 1. 解析到前端传递的评论的动态id和用户的评论正文String movementId = map.get("movementId").toString();String comment = map.get("comment").toString();// 2. 调用Service方法this.commentService.publishComment(movementId, comment);// 3. 构建返回值return ResponseEntity.ok(null);
}

Service

/*** 根据动态id和评论正文 新增一条评论** @param movementId 动态id* @param comment    评论正文*/
public Integer publishComment(String movementId, String comment) {// 1. 根据动态ID查询到动态的对象Comment newComment = new Comment();// 4. 继续封装其他的Comment属性newComment.setPublishId(new ObjectId(movementId));newComment.setContent(comment);newComment.setUserId(UserHolder.getUserId());newComment.setCreated(System.currentTimeMillis());// 5. 调用方法保存Comment, 并且返回保存后的评论数return this.commentApi.save(newComment, CommentType.COMMENT.getType());
}

API层之前已经展示

4.2 根据动态查询评论列表

需求:当用户点击某一个动态时,会显示动态详情和对应的评论列表。接口如下:

代码实现

Controller

/*** 根据动态ID查询到动态的所有的评论列表* @param page 页号* @param pagesize 页大小* @param movementId 动态ID* @return*/
@GetMapping
public ResponseEntity getCommentListByMovementId(@RequestParam(defaultValue = "1") Integer page,@RequestParam(defaultValue = "5") Integer pagesize,String movementId) {// 1. 调用Service方法查询到CommentVO集合的PageResultPageResult result = this.commentService.getCommentListByMovementId(page,pagesize, movementId);// 2. 返回结果return ResponseEntity.ok(result);
}

Service

/**
* 根据动态ID查询到动态的所有的评论列表
*
* @param page       页号
* @param pagesize   页大小
* @param movementId 动态ID
* @return
*/
public PageResult getCommentListByMovementId(Integer page, Integer pagesize, String movementId) {// 1. 根据动态ID查询到所有的评论集合List<Comment> commentList = this.commentApi.getCommentListByMovementId(page, pagesize, movementId);if (CollUtil.isEmpty(commentList)) {return new PageResult();}// 2. 从评论集合中抽取出userId评论发布人的idList<Long> userIds = CollUtil.getFieldValues(commentList, "userId", Long.class);// 3. 根据发布人的Id去查询对应的用户详情Map<Long, UserInfo> userInfoByIds = this.userInfoApi.getUserInfoByIds(userIds, null);// 4. 封装Vo对象List<CommentVo> commentVos = new ArrayList<>();for (Comment comment : commentList) {Long userId = comment.getUserId();UserInfo userInfo = userInfoByIds.get(userId);if (userInfo != null) {CommentVo init = CommentVo.init(userInfo, comment);String key = MOVEMENTS_INTERACT_KEY + comment.getId();// 构造哈希key prefix + userIdString likeHashKey = MOVEMENT_LIKE_HASHKEY + userId;if (this.redisTemplate.opsForHash().hasKey(key, likeHashKey)) {init.setHasLiked(1);}commentVos.add(init);}}// 5. 返回结果return new PageResult(page, pagesize, 0, commentVos);
}

注意:当我们查询评论的时候,还需要从Redis中查询登录用户是否对这条评论点过赞,如果点过赞,则需要设置相关属性。因此我们修改查询动态列表中的相关代码。

5. 代码的封装

由于目前无论是点赞,还是喜欢,还是评论,实际上操作差不多,代码存在很多的冗余,因此考虑将代码封装起来复用。

首先在CommentService中封装一个方法用来处理comment

/**
* 处理喜欢和点赞请求
*
* @param movementId 动态id
* @param type       类型 判断是喜欢还是点赞
* @param flag       标志位 true表示新增 false表示删除
* @return
*/
private Integer processLikeOrLove(String movementId, Integer type, Boolean flag) {// 1. 根据用户id,判断用户是否已经喜欢过或者点过赞Long userId = UserHolder.getUserId();Boolean check = this.commentApi.check(movementId, type, userId);if (!flag) {check = !check;}if (check) {switch (type) {case 1:case 4:throw new BusinessException(ErrorResult.likeError());case 3:throw new BusinessException(ErrorResult.loveError());}}// 2. 用户没有点过赞或者喜欢过  封装Comment对象,调用api保存commentComment comment = new Comment();comment.setPublishId(new ObjectId(movementId));comment.setUserId(userId);comment.setCreated(System.currentTimeMillis());// 3. 设置RedisString key = MOVEMENTS_INTERACT_KEY + movementId;// 构造哈希key prefix + userIdString likeHashKey = MOVEMENT_LIKE_HASHKEY + userId;String loveHashKey = MOVEMENT_LOVE_HASHKEY + userId;Integer count;if (flag) {// 插入数据count = this.commentApi.save(comment, type);if (type == CommentType.LOVE.getType()) {this.redisTemplate.opsForHash().put(key, loveHashKey, "1");} else if (type == CommentType.LIKE.getType() || type == CommentType.LIKECOMMENT.getType()) {this.redisTemplate.opsForHash().put(key, likeHashKey, "1");}} else {// 删除数据count = this.commentApi.delete(comment, type);if (type == CommentType.LOVE.getType()) {this.redisTemplate.opsForHash().delete(key, loveHashKey);} else if (type == CommentType.LIKE.getType() || type == CommentType.LIKECOMMENT.getType()) {this.redisTemplate.opsForHash().delete(key, likeHashKey);}}return count;
}

然后在API层封装保存comment和删除commet的通用方法

/**
* 保存Comment到数据库
*
* @param comment 评论对象
* @param type    评论类型
* @return
*/
@Override
public Integer save(Comment comment, int type) {// 1. 从Comment对象中获取到动态id,查询动态id,try {ObjectId publishId = comment.getPublishId();Movement movementById = this.mongoTemplate.findById(publishId, Movement.class);if (movementById != null || type == CommentType.LIKECOMMENT.getType()) {if (movementById == null) {// 1. 获取到动态id的作者 并设置到Comment的publishUserId字段中Comment publishUser = this.mongoTemplate.findById(comment.getPublishId(), Comment.class);if (publishUser != null) {comment.setPublishUserId(publishUser.getPublishUserId());} else {return 0;}} else {comment.setPublishUserId(movementById.getUserId());}// 2. 设置评论的类型comment.setCommentType(type);// 3. 保存到数据库this.mongoTemplate.save(comment);// 4. 根据不同的评论类型,更新Movement或者Comment表中数据表中对象的记录// 4.1 构造查询条件 如果是动态的点赞或者喜欢if (type == CommentType.LIKECOMMENT.getType()) {// 评论点赞Query query = new Query(Criteria.where("id").is(comment.getPublishId()));Update update = new Update();update.inc("likeCount", 1);FindAndModifyOptions findAndModifyOptions = FindAndModifyOptions.options().returnNew(true);return this.mongoTemplate.findAndModify(query, update, findAndModifyOptions, Comment.class).getLikeCount();} else {Criteria criteria = Criteria.where("id").is(movementById.getId());Query query = new Query(criteria);Update update = new Update();Integer commentType = comment.getCommentType();if (commentType == CommentType.LIKE.getType()) {update.inc("likeCount", 1);} else if (commentType == CommentType.COMMENT.getType()) {update.inc("commentCount", 1);} else {update.inc("loveCount", 1);}FindAndModifyOptions findAndModifyOptions = FindAndModifyOptions.options().returnNew(true);// 调用template更新 返回更新后的Movement对象Movement modify = this.mongoTemplate.findAndModify(query, update, findAndModifyOptions, Movement.class);// 根据不同的评论类型,返回对应的计数return modify.getCount(commentType);}} else {return 0;}} catch (Exception e) {throw new RuntimeException(e);}
}
/**
* 取消点赞 喜欢 评论点赞
*
* @param comment 要删除的评论,喜欢 评论点赞
* @return 取消点赞之后最新的点赞数量
*/
@Override
public Integer delete(Comment comment, int type) {// 1. 解析数据ObjectId publishId = comment.getPublishId();Long userId = comment.getUserId();// 2. 构造条件Criteria criteria = Criteria.where("publishId").is(publishId).and("userId").is(userId).and("commentType").is(type);Query query = new Query(criteria);// 3. 删除comment中数据this.mongoTemplate.remove(query, Comment.class);if (type == CommentType.LIKECOMMENT.getType()) {Query modifyQuery = new Query(Criteria.where("id").is(publishId));Update update = new Update();update.inc("likeCount", -1);FindAndModifyOptions findAndModifyOptions = FindAndModifyOptions.options().returnNew(true);return this.mongoTemplate.findAndModify(modifyQuery, update, findAndModifyOptions, Comment.class).getLikeCount();} else {// 4. 更新相应的movement中数据Query modifyQuery = new Query(Criteria.where("id").is(publishId));Update update = new Update();Integer commentType = type;if (commentType == CommentType.LIKE.getType()) {update.inc("likeCount", -1);} else if (commentType == CommentType.COMMENT.getType()) {update.inc("commentCount", -1);} else {update.inc("loveCount", -1);}// 调用template更新 返回更新后的Movement对象FindAndModifyOptions findAndModifyOptions = FindAndModifyOptions.options().returnNew(true);Movement modify = this.mongoTemplate.findAndModify(modifyQuery, update, findAndModifyOptions, Movement.class);// 5.返回结果return modify.getCount(type);}
}

【探花交友DAY 06】圈子中的互动功能(点赞、评论、喜欢)相关推荐

  1. 探花交友_第7章-完善消息功能以及个人主页

    探花交友_第7章-完善消息功能以及个人主页 文章目录 探花交友_第7章-完善消息功能以及个人主页 1.消息点赞.喜欢.评论列表 1.1.dubbo服务 1.1.1.定义接口 1.1.2.编写实现 1. ...

  2. 【探花交友】day05—圈子互动

    目录 课程说明 1.动态查询 1.1.查询好友动态 1.2.查询推荐动态 1.3.根据id查询动态 2.圈子互动 2.1.环境搭建 2.2.动态评论 2.3.点赞 2.4.喜欢 课程说明 圈子动态查询 ...

  3. 探花交友_第10章_搭建后台系统(新版)

    探花交友_第10章_搭建后台系统(新版) 文章目录 探花交友_第10章_搭建后台系统(新版) 1.1 概述 1.2 API网关 1.2.1 搭建网关 依赖 引导类 跨域问题配置类 配置文件 测试 1. ...

  4. 探花交友_第7章_即时通信(新版)

    探花交友_第7章_即时通信 文章目录 探花交友_第7章_即时通信 1.1 什么是即时通信 1.2 功能说明 1.3 技术方案 2. 环信 2.1 开发简介 2.2 环信Console 2.3 环信AP ...

  5. 探花交友_第6章_圈子互动(新版)

    探花交友_第6章_圈子互动(新版) 文章目录 探花交友_第6章_圈子互动(新版) 课程说明 1. 动态查询 1.1 查询好友动态 1.1.1 接口文档 1.1.2 代码步骤 1.1.3 代码实现 ta ...

  6. java 探花交友项目day5 推荐好友列表 MongoDB集群 发布动态,查询动态 圈子功能

    推荐好友列表 需求分析 推荐好友:分页形式查询推荐的用户列表,根据评分排序显示 代码实现: tanhuaController: /**  * 查询分页推荐好友列表  */ @GetMapping(&q ...

  7. 黑马探花交友----3.圈子-发布动态点赞评论

    学习目标: 圈子功能说明 圈子技术实现 圈子技术方案 圈子实现发布动态 圈子实现好友动态 圈子实现推荐动态 圈子实现点赞.喜欢功能 圈子实现评论 圈子实现评论的点赞 1.功能说明 探花交友项目中的圈子 ...

  8. 探花交友_第5章_圈子、小视频功能实现

    探花交友_第5章_圈子.小视频功能实现 文章目录 探花交友_第5章_圈子.小视频功能实现 1.圈子点赞实现分析 2.点赞 2.1.定义枚举 2.2.dubbo服务 2.2.1.定义接口 2.2.2.编 ...

  9. 探花交友_第4章_圈子功能实现

    探花交友_第4章_圈子功能实现 文章目录 探花交友_第4章_圈子功能实现 1.抽取common工程 1.1.创建my-tanhua-common工程 1.2.通用枚举 1.3.抽取mapper 1.4 ...

最新文章

  1. 最佳时间 (DOM编程艺术)
  2. 关于激励函数的一些思考
  3. 深入浅出requireJS-1
  4. php筛选怎么做,thinkphp条件筛选 例子
  5. 医疗AI市场三年内规模可达66亿美元,哪些应用最有潜力?| 报告
  6. Lightroom Classic 教程,如何使用 Photoshop 和 Lightroom 优化照片?
  7. [ 4w字 ] JavaSE总结(基础+高级+多线程+面试题)
  8. python界面实现点餐系统_餐厅点餐系统详细设计与系统实现
  9. 三、FreeNas实现SMB共享、FTP搭建实验报告
  10. SASS教程sass超详细教程
  11. 网络视频会议软件哪个好?欢迎大家补位!
  12. 百度竞价有没有好的推广方法?
  13. python中count方法
  14. 智慧景区人员定位方案
  15. 水晶报表设置纸张大小
  16. 微信头像失效_微信头像地址失效踩坑记附带方案
  17. 本地搭建xxl-job服务及连接验证
  18. uniapp去掉返回键
  19. ARC101E - Ribbons on Tree 树形DP
  20. python爬虫之基于JS加密破解--有道翻译/百度翻译

热门文章

  1. 用贝塞尔曲线玩出来的花样简直不要太美
  2. 想拿到三万月薪在北上深杭做Java开发如何,需要什么程度技术?
  3. ubuntu 安装 英伟达NVIDIA驱动
  4. server2008r2文件服务器,server2008r2文件服务器监控
  5. 面向对象的常量类——枚举(Enum)
  6. 3dmax:3dmax三维VR渲染设置之高级灯光渲染(经典案例—利用自由聚光灯制作筒灯效果效果)图文教程
  7. 一对一培训机构如何通过系统来完成课时记录?
  8. 尚硅谷python培训多少钱
  9. zynq自定义PL IP核linux驱动开发流程
  10. win10已达到计算机的连接数量最大值,win10系统共享提示“达到连接数目限制”的办法...