探花交友_第5章_圈子功能实现(新版)

news2024/11/16 18:01:28

探花交友_第5章_圈子功能实现(新版)

文章目录

  • 探花交友_第5章_圈子功能实现(新版)
    • 课程说明
    • 1、首页推荐
      • 1.1、接口分析
      • 1.2、功能实现
        • 1.2.1 controller
        • 1.2.2 service
        • 1.2.3 API接口
        • 1.2.4 请求dto对象
    • 2、圈子功能
      • 2.1、功能说明
      • 1.2、实现方案分析
      • 1.3、技术方案(重点)
      • 1.4、表结构设计
    • 3、圈子实现
      • 3.1、环境搭建
        • 3.1.1、mongo主键自增
        • 3.1.2、实体类
          • Movement
          • MovementTimeLine
          • Friend
        • 3.1.3、API接口
        • 3.1.4、API实现类
        • 3.1.5、MovementsController
        • 3.1.6、MovementsService
      • 3.2、发布动态
        • 3.2.0、思路步骤
        • 3.2.1、MovementsController
        • 3.2.2、编写service
        • 3.2.3、API层
        • 3.2.4、异步处理工具类
        • 3.2.5、整合测试
      • 3.3、查询个人动态
        • 3.3.0、思路分析
        • 3.3.1、vo对象
        • 3.3.2、controller
        • 3.3.3、service
        • 3.3.4、API层
        • 3.3.5、测试

课程说明

  • 首页推荐
  • MongoDB集群
  • 圈子功能说明
  • 圈子技术实现与方案
  • 圈子实现发布动态
  • 圈子实现个人动态

1、首页推荐

在这里插入图片描述

1.1、接口分析

地址:http://192.168.136.160:3000/project/19/interface/api/118

在这里插入图片描述
在这里插入图片描述

响应:

{
    "counts": 4698,
    "pagesize": 20,
    "pages": 58,
    "page": 16,
    "items": [
        {
            "id": 1011,
            "avatar": "assets/images/avatar_2.png",
            "nickname": "黑马小妹",
            "gender": "woman",
            "age": 23,
            "tags": [
                "本科",
                "年龄相仿",
                "单身"
            ],
            "fateValue": 96
        },
        {
            "id": 2495,
            "avatar": "assets/images/avatar_1.png",
            "nickname": "米朵妹妹",
            "gender": "man",
            "age": 28,
            "tags": [
                "年龄相仿",
                "本科",
                "单身"
            ],
            "fateValue": 87
        },
        {
            "id": 5708,
            "avatar": "assets/images/avatar_4.png",
            "nickname": "黑马小妹",
            "gender": "man",
            "age": 24,
            "tags": [
                "单身",
                "本科",
                "年龄相仿"
            ],
            "fateValue": 94
        },
        {
            "id": 4768,
            "avatar": "assets/images/avatar_3.png",
            "nickname": "黑马小妹",
            "gender": "man",
            "age": 24,
            "tags": [
                "年龄相仿",
                "单身",
                "本科"
            ],
            "fateValue": 80
        }
    ]
}

1.2、功能实现

1.2.1 controller

TanhuaController编写推荐列表方法

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

1.2.2 service

TanhuaService编写推荐列表方法

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

1.2.3 API接口

RecommendUserApi 接口和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<RecommendUser> list = mongoTemplate.find(query, RecommendUser.class);
    long count = mongoTemplate.count(query, RecommendUser.class);
    //4、构建返回值PageResult
    return  new PageResult(page,pagesize,count,list);
}

1.2.4 请求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; //学历
}

2、圈子功能

2.1、功能说明

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

在这里插入图片描述

发布:

在这里插入图片描述

1.2、实现方案分析

对于圈子功能的实现,我们需要对它的功能特点做分析:

  • 数据量会随着用户数增大而增大
  • 读多写少
  • 非好友看不到其动态内容
  • ……

针对以上特点,我们来分析一下:

  • 对于数据量大而言,显然不能够使用关系型数据库进行存储,我们需要通过MongoDB进行存储
  • 对于读多写少的应用,需要减少读取的成本
    • 比如说,一条SQL语句,单张表查询一定比多张表查询要快
  • 对于每个人数据在存储层面最好做到相互隔离,这样的话就不会有影响

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

  • 发布表:记录了所有用户的发布的东西信息,如图片、视频等。
  • 自己时间线:相册是每个用户独立的,记录了该用户所发布的所有内容。
  • 好友时间线:所谓“刷朋友圈”,就是刷时间线,就是一个用户所有的朋友的发布内容。
  • 好友表:记录好友关系

1.3、技术方案(重点)

根据之前我们的分析,对于技术方案而言,将采用MongoDB+Redis来实现,其中MongoDB负责存储,Redis负责缓存数据。

1.4、表结构设计

发布表:动态总记录表(记录每个人发送的动态详情)

#表名:movement
{
    "_id": ObjectId("5e82dc416401952928c211d8"),
    "pid": NumberLong("10064"),
    "userId": NumberLong("6"),
    "textContent": "最悲伤却又是最痛苦的谎言,就是我还好,没有关系。",
    "medias": [
        "https://tanhua-dev.oss-cn-zhangjiakou.aliyuncs.com/photo/7/1.jpg",
        "https://tanhua-dev.oss-cn-zhangjiakou.aliyuncs.com/photo/7/1564567349498.jpg",
        "https://tanhua-dev.oss-cn-zhangjiakou.aliyuncs.com/photo/7/1564567352977.jpg",
        "https://tanhua-dev.oss-cn-zhangjiakou.aliyuncs.com/photo/7/1564567360406.jpg"
    ],
    "longitude": "121.588627",
    "latitude": "30.935781",
    "state": NumberInt("0"),
    "locationName": "中国上海市奉贤区人民路445弄",
    "created": NumberLong("1585634369493"),
    "_class": "com.tanhua.dubbo.server.pojo.Publish"
}

好友时间线表:记录当前好友发布的动态数据

#表名:movement_timeline
{
    "_id": ObjectId("609cf6538743d448c02c61f0"),
    "movementId": ObjectId("609cf6538743d448c02c61ef"),
    "userId": NumberLong("106"),
    "friendId": NumberLong("1"),
    "created": NumberLong("1620899411043"),
    "_class": "com.tanhua.model.mongo.MovementTimeLine"
}

好友关系表:记录好友的双向关系(双向)

#表名:friend
{
    "_id": ObjectId("6018bc055098b2230031e2da"),
    "created": NumberLong("1612233733056"),
    "userId": NumberLong("1"),
    "friendId": NumberLong("106"),
    "_class": "com.itheima.domain.mongo.Friend"
}

3、圈子实现

3.1、环境搭建

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

3.1.1、mongo主键自增

第一步:创建实体类

package com.tanhua.domain.mongo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.bson.types.ObjectId;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;

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

    private ObjectId id;

    private long seqId; //自增序列

    private String collName;  //集合名称
}

第二步:编写service

package com.tanhua.dubbo.utils;

import com.tanhua.domain.mongo.Sequence;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.FindAndModifyOptions;
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.stereotype.Component;

@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();
    }
}

3.1.2、实体类

写到tanhua-domain工程中:

Movement

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

package com.tanhua.domain.mongo;

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

import java.util.List;

//动态详情表
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "movement")
public class Movement implements java.io.Serializable {


    private ObjectId id; //主键id
    private Long pid; //Long类型,用于推荐系统的模型(自动增长)
    private Long created; //发布时间
    private Long userId;
    private String textContent; //文字
    private List<String> medias; //媒体数据,图片或小视频 url
    private String longitude; //经度
    private String latitude; //纬度
    private String locationName; //位置名称
    private Integer state = 0;//状态 0:未审(默认),1:通过,2:驳回
}
MovementTimeLine

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

package com.tanhua.domain.mongo;

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

/**
 * 好友时间线表,用于存储好友发布的数据
 */
@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

Friend 好友关系表

package com.tanhua.domain.mongo;

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

/**
 * 好友表:好友关系表
 */
@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; //时间

}

3.1.3、API接口

package com.tanhua.dubbo.api.mongo;

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

public interface MovementApi {

}

3.1.4、API实现类

package com.tanhua.dubbo.api.mongo;

import com.tanhua.domain.mongo.*;
import com.tanhua.domain.vo.PageResult;
import com.tanhua.dubbo.utils.IdService;
import org.apache.dubbo.config.annotation.Service;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
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 java.util.ArrayList;
import java.util.List;

@Service
public class MovementApiImpl implements PublishApi {

}

3.1.5、MovementsController

package com.tanhua.server.controller;

import com.tanhua.domain.mongo.Publish;
import com.tanhua.server.service.MovementsService;
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.io.IOException;

@RestController
@RequestMapping("/movements")
public class MovementsController {

    @Autowired
    private MovementsService movementsService;

}

3.1.6、MovementsService

package com.tanhua.server.service;

import com.tanhua.autoconfig.templates.OssTemplate;
import com.tanhua.domain.db.UserInfo;
import com.tanhua.domain.mongo.Publish;
import com.tanhua.domain.vo.Movements;
import com.tanhua.domain.vo.PageResult;
import com.tanhua.dubbo.api.UserInfoApi;
import com.tanhua.dubbo.api.mongo.PublishApi;
import com.tanhua.server.interceptor.UserHolder;
import io.jsonwebtoken.lang.Collections;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;

@Service
public class MovementsService {

    @Autowired
    private OssTemplate ossTemplate;

    @DubboReference
    private MovementApi movementApi;

    @DubboReference
    private UserInfoApi userInfoApi;

    @Autowired
    private RedisTemplate<String,String> redisTemplate;
}

3.2、发布动态

后续的测试:使用106(13800138000)和1号用户(13500000000)

3.2.0、思路步骤

在这里插入图片描述

3.2.1、MovementsController

tanhua-server工程编写MovementsController,完成发布动态功能

@RestController
@RequestMapping("/movements")
public class MovementController {

    @Autowired
    private MovementService movementService;

    @Autowired
    private CommentsService commentsService;

    /**
     * 发布动态
     */
    @PostMapping
    public ResponseEntity movements(Movement movement,
                                    MultipartFile imageContent[]) throws IOException {
        movementService.publishMovement(movement,imageContent);
        return ResponseEntity.ok(null);
    }
}

3.2.2、编写service

tanhua-server工程编写MovementsService,完成发布动态功能

/**
 * 发布动态
 */
public void publishMovement(Movement movement, MultipartFile[] imageContent) throws IOException {
    //1、判断发布动态的内容是否存在
    if(StringUtils.isEmpty(movement.getTextContent())) {
        throw  new BusinessException(ErrorResult.contentError());
    }
    //2、获取当前登录的用户id
    Long userId = UserHolder.getUserId();
    //3、将文件内容上传到阿里云OSS,获取请求地址
    List<String> 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);
}

3.2.3、API层

    //发布动态
    public void publish(Movement movement) {
        //1、保存动态详情
        try {
            //设置PID
            movement.setPid(idWorker.getNextId("movement"));
            //设置时间
            movement.setCreated(System.currentTimeMillis());
            //movement.setId(ObjectId.get());
            mongoTemplate.save(movement);
           //2、查询当前用户的好友数据
//            Criteria criteria = Criteria.where("userId").is(movement.getUserId());
//            Query query = Query.query(criteria);
//            List<Friend> friends = mongoTemplate.find(query, Friend.class);
            //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());
//                mongoTemplate.save(timeLine);
//            }
            //写入好友的时间线表(异步写入)
            timeLineService.saveTimeLine(movement.getUserId(), movement.getId());
        } catch (Exception e) {
            //忽略事务处理
            e.printStackTrace();
        }
    }

3.2.4、异步处理工具类

@Component
public class TimeLineService {
    @Autowired
    private MongoTemplate mongoTemplate;

    @Async //异步执行,原理:底层开一个线程去执行该方法
    public CompletableFuture<String> saveTimeLine(Long userId, ObjectId movementId) {
        //写入好友的时间线表
        try {
            //1、查询当前用户的好友数据
            Criteria criteria = Criteria.where("userId").is(userId);
            Query query = Query.query(criteria);
            List<Friend> friends = mongoTemplate.find(query, Friend.class);
            if (CollUtil.isEmpty(friends)) {
                return CompletableFuture.completedFuture("ok");//返回成功
            }

            //2、循环好友数据,构建时间线数据存入数据库
            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);
            }
        } catch (Exception e) {
           e.printStackTrace();
            //TODO 事务回滚问题
            return CompletableFuture.completedFuture("error");
        }

        return CompletableFuture.completedFuture("ok");
    }
}

3.2.5、整合测试

@RunWith(SpringRunner.class)
@SpringBootTest(classes = AppServerApplication.class)
public class MovementApiTest {

    @DubboReference
    private MovementApi movementApi;

    @Test
    public void testPublish() {
        Movement movement = new Movement();
        movement.setUserId(106l);
        movement.setTextContent("你的酒窝没有酒,我却醉的像条狗");
        List<String> list = new ArrayList<>();
        list.add("https://tanhua-dev.oss-cn-zhangjiakou.aliyuncs.com/images/tanhua/avatar_1.png");
        list.add("https://tanhua-dev.oss-cn-zhangjiakou.aliyuncs.com/images/tanhua/avatar_2.png");
        movement.setMedias(list);
        movement.setLatitude("40.066355");
        movement.setLongitude("116.350426");
        movement.setLocationName("中国北京市昌平区建材城西路16号");
        movementApi.publish(movement);
    }
}

3.3、查询个人动态

查询好友动态其实就是查询自己的时间线表,好友在发动态时已经将动态信息写入到了自己的时间线表中。

3.3.0、思路分析

在这里插入图片描述

3.3.1、vo对象

package com.tanhua.model.vo;

import com.tanhua.model.domain.UserInfo;
import com.tanhua.model.mongo.Movement;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.util.StringUtils;

import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Date;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class MovementsVo  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 Integer likeCount; //点赞数
    private Integer commentCount; //评论数
    private Integer loveCount; //喜欢数


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


    public static MovementsVo init(UserInfo userInfo, Movement item) {
        MovementsVo vo = new MovementsVo();
        //设置动态数据
        BeanUtils.copyProperties(item, vo);
        vo.setId(item.getId().toHexString());
        //设置用户数据
        BeanUtils.copyProperties(userInfo, vo);
        if(!StringUtils.isEmpty(userInfo.getTags())) {
            vo.setTags(userInfo.getTags().split(","));
        }
        //图片列表
        vo.setImageContent(item.getMedias().toArray(new String[]{}));
        //距离
        vo.setDistance("500米");
        Date date = new Date(item.getCreated());
        vo.setCreateDate(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(date));
        //设置是否点赞(后续处理)
        vo.setHasLoved(0);
        vo.setHasLiked(0);
        return vo;
    }
}

3.3.2、controller

修改MovementsController完成查询好友动态功能

/**
 * 查询我的动态
 */
@GetMapping("/all")
public ResponseEntity findByUserId(Long userId,
                                   @RequestParam(defaultValue = "1") Integer page,
                                   @RequestParam(defaultValue = "10") Integer pagesize) {
    PageResult pr = movementService.findByUserId(userId,page,pagesize);
    return ResponseEntity.ok(pr);
}

3.3.3、service

修改MovementsService完成查询好友动态功能

//查询个人动态
public PageResult findByUserId(Long userId, Integer page, Integer pagesize) {
    //1、根据用户id,调用API查询个人动态内容(PageResult  -- Movement)
    PageResult pr = movementApi.findByUserId(userId,page,pagesize);
    //2、获取PageResult中的item列表对象
    List<Movement> items = (List<Movement>) pr.getItems();
    //3、非空判断
    if(items == null) {
        return pr;
    }
    //4、循环数据列表
    UserInfo userInfo = userInfoApi.findById(userId);
    List<MovementsVo> vos = new ArrayList<>();
    for (Movement item : items) {
        //5、一个Movement构建一个Vo对象
        MovementsVo vo = MovementsVo.init(userInfo, item);
        vos.add(vo);
    }
    //6、构建返回值
    pr.setItems(vos);
    return pr;
}

3.3.4、API层

修改PublishApiPublishApiImpl完成查询好友动态功能

@Override
public PageResult findByUserId(Long userId, Integer page, Integer pagesize) {
    Criteria criteria = Criteria.where("userId").is(userId);
    Query query = Query.query(criteria).skip((page -1 ) * pagesize).limit(pagesize)
            .with(Sort.by(Sort.Order.desc("created")));
    List<Movement> movements = mongoTemplate.find(query, Movement.class);
    return new PageResult(page,pagesize,0l,movements);
}

3.3.5、测试

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/63807.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

js input 正则保留2位小数中文拼音输入问题 + 限制输入整数的方案

js input 正则保留2位小数中文拼音输入问题 限制输入整数的方案 problem 背景 element ui el-input组件原生input事件 需求 限制输入框的输入只允许输入数字和小数点保留2位小数&#xff0c;不允许输入第3位小数 实现 使用 input 原生 oninput 事件使用 js 正则匹配 /…

二分图(Bipartite Graph)

预计阅读时间&#xff1a;10分钟 一、简介 二分图の定义 二分图又叫二部图&#xff0c;是图论中的一种特殊模型。 假设S(V,E)是一个无向图。如果顶点V可分割为两个互不相交的子集(A,B)&#xff0c;并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集(i in…

常见的限流算法分析以及手写实现(计数器、漏斗、令牌桶)

常见的限流算法分析 限流在我们日常生活中经常见到&#xff0c;如火车站门口的栏杆、一些景点的门票只出售一定的数量 等等。在我们的开发中也用到了这种思想。 为什么要限流 &#x1f3eb;在保证可用的情况下尽可能多增加进入的人数,其余的人在排队等待,或者返回友好提示,保…

[附源码]JAVA毕业设计楼宇管理系统(系统+LW)

[附源码]JAVA毕业设计楼宇管理系统&#xff08;系统LW&#xff09; 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a;…

Python笔记 · 魔法函数 / Magic Methods / Dunder Methods

在上篇文章《Python笔记 私有方法、私有属性 & 单下划线、双下划线》我们介绍过以前置双下划线开始&#xff0c;后置双下划线结束的方法名&#xff1a;__*__&#xff0c;这是系统定义的一批特殊函数&#xff0c;通常被称之为&#xff1a;魔法函数&#xff08;Magic Method…

5-2:Kafka入门

Kafka简介 原本的kafka只是一个处理消息队列的技术&#xff0c;但随着功能不断增加&#xff0c;不断综合&#xff0c;成为了一个分布式的流媒体平台 Kafka是一个分布式的流媒体平台。 应用&#xff1a;消息系统、日志收集、用户行为追踪、流式处理。 Kafka特点 高吞吐量、消息…

电源模块测试解决方案-电源测试系统方案-电源模块测试报告NSAT-8000

*测试仪器&#xff1a;可编程直流电源、可编程直流电子负载、数字示波器、功率计 *测试产品&#xff1a;电源模块。纳米软件电源ATE自动测试系统适用于大功率工业电源、AC/DC类DC电源供应器、适配器、充电器、LED电源等开关电源之综合性能测试。 *被测项目&#xff1a;有效值电…

目标检测之多尺度融合

多尺度 卷积神经网络通过逐层抽象的方式来提取目标的特征&#xff0c;其中一个重要的概念就是感受野。 高层网络的感受野比较大&#xff0c;语义信息表征能力强&#xff0c;但是特征图的分辨率低&#xff0c;几何信息的表征能力弱&#xff08;空间几何特征细节缺乏&#xff09…

深入React源码揭开渲染更新流程的面纱

转前端一年半了&#xff0c;平时接触最多的框架就是React。在熟悉了其用法之后&#xff0c;避免不了想深入了解其实现原理&#xff0c;网上相关源码分析的文章挺多的&#xff0c;但是总感觉不如自己阅读理解来得深刻。于是话了几个周末去了解了一下常用的流程。也是通过这篇文章…

深入了解Spring循环依赖本质

说明: 1. 本文基于Spring-Framework 5.1.x版本讲解 2. 建议读者对创建对象部分源码有一定了解 概述 这篇讲讲Spring循环依赖的问题&#xff0c;网上讲循环依赖的帖子太多太多了&#xff0c;相信很多人也多多少少了解一点&#xff0c;那我还是把这个问题自己梳理一遍&#xff…

kubernetes,service详解下

kubernetes&#xff0c;service详解下 HeadLiness类型的Service 在某些场景中&#xff0c;开发人员可能不想使用Service提供的负载均衡功能&#xff0c;而希望自己来控制负载均衡策略&#xff0c;针对这种情况&#xff0c;kubernetes提供了HeadLiness Service&#xff0c;这类…

内存分段与内存分页:逻辑地址、物理地址、线性地址、虚拟地址

这篇文章也是我自己的博客网站的里的文章&#xff0c;我觉得这篇文章还是我觉得知识含量比较高的文章&#xff0c;所以特地把它发出来看看。 这篇文章写于我在写自己的操作系统JackOS的时候系统梳理了一下CPU访问内存的各种方式&#xff0c;写完这篇文章之后&#xff0c;我对C…

Kafka高级特性解析之生产者

1、消息发送 1.1、数据生产流程解析 Producer创建时&#xff0c;会创建一个Sender线程并设置为守护线程。生产消息时&#xff0c;内部其实是异步流程&#xff1b;生产的消息先经过拦截器->序列化器->分区器&#xff0c;然后将消息缓存在缓冲区&#xff08;该缓冲区也是在…

Docker桌面版安装与使用(windows)

目录一、Docker概念二、下载安装三、docker镜像安装与操作四、制作自己的python镜像容器五、目录挂载六、多容器通信七、Docker-Compose管理多个容器运行八、发布和部署九、备份数据迁移一、Docker概念 1、Docker 是一个应用打包、分发、部署的工具2、镜像Image、容器Containe…

Windows OpenGL 图像绿幕抠图

目录 一.OpenGL 图像绿幕抠图 1.原始图片2.效果演示 二.OpenGL 图像绿幕抠图源码下载三.猜你喜欢 零基础 OpenGL ES 学习路线推荐 : OpenGL ES 学习目录 >> OpenGL ES 基础 零基础 OpenGL ES 学习路线推荐 : OpenGL ES 学习目录 >> OpenGL ES 特效 零基础 Open…

[问题解决方案](多人共同合并场景)git已merge到master分支代码且被同事代码覆盖如何回退

git已merge到master分支代码如何回退&#xff08;多人共同合并&#xff09;场景已经被同事代码覆盖的解决方案&#xff08;无需强制合并权限&#xff09;代码revert后又需要重新启用怎么办如果是未受保护分支代码的回退且只有你一人合并的代码 可以直接使用下面的命令即可如果只…

【Unity3D日常开发】Unity3D中实现不规则Button按钮的精准响应

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址我的个人博客QQ群&#xff1a;1040082875 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 在使用Unity3D开发中&#xff0c;可…

全志V853平台Camera模块开发框架详解

Camera 本章节介绍V853平台 Camera 模块的开发。 V853支持并口CSI、MIPI&#xff0c;使用VIN camera驱动框架。 Camera通路框架 VIN支持灵活配置单/双路输入双ISP多通路输出的规格 引入media框架实现pipeline管理 将libisp移植到用户空间解决GPL问题 将统计buffer独立为v…

Web大学生网页作业成品——抗击疫情网站设计与实现(HTML+CSS)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

Vue3 样式绑定

Vue3 样式绑定1.Vue.js class2.class 属性绑定3.Vue.js style(内联样式)4.组件上使用 class 属性1.Vue.js class class 与 style 是 HTML 元素的属性&#xff0c;用于设置元素的样式&#xff0c;我们可以用 v-bind 来设置样式属性。 v-bind 在处理 class 和 style 时&#xf…