redis黑马点评项目启动指南(含mac m1pro | windows11 wsl2 ubuntu环境配置 持续更新中~)

redis黑马点评项目学习笔记 mac m1pro windows 含项目配置教学

  • mac M1pro环境配置
  • windows11 wsl2 ubuntu 环境配置
  • 一.短信登录
    • 1. 1发送验证码
    • 1.2短信登录+注册
    • 1.3登录校验拦截器
    • 补缺Cookie Session Token
    • 1.4基于redis+token认证实现短信登陆
    • 1.5完善token认证的刷新机制
  • 2.商户查询缓存
    • 2.1添加商户缓存(到redis中)

mac M1pro环境配置

  1. 视频里用的是虚拟机,但我不想配虚拟机,太臃肿了,我选择docker这里我用的是OrbStack,这款软件使用的是rust编写,性能占用低(相比于dockerDesktop)
    brew insall orbstack或者去官网下载

  2. docker拉取redis 和 mysql镜像,注意常规镜像pull下来run后使用的是rosetta转译,性能会下降.
    因此我们拉取arm64v8/mysql和arm64v8/redis这两个镜像
    pull docker pull arm64v8/mysql
    pull docker pull arm64v8/redis
    在这里插入图片描述
    这里种类是Apple说明没有经过转译在这里插入图片描述

  3. 部署mysql和redis

    docker run -p 3306:3306 \
     --name mysql \
     -v mysql_data:/var/lib/mysql \
     -v mysql_conf:/etc/mysql/conf.d \
     --privileged=true \
     -e MYSQL_ROOT_PASSWORD=123456 \
     -d arm64v8/mysql
    
    docker run -p 6379:6379 \
    --name redis \
    -v redis_data:/data \
    -v redis_conf:/etc/redis/redis.conf \
    -d arm64v8/redis \
    redis-server /etc/redis/redis.conf
    

    使用docker ps查看是否成功
    在这里插入图片描述

  4. 进入mysql数据库中导入sql表并运行sql表语句

    # 1.拷贝SQL文件到mysql容器中(在容器外,自己的sql文件所在位置,mysql是容器名)
    docker cp yyy.sql mysql:/hmdp.sql
    
    # 2. 创建数据库(第一个mysql是容器名,第二个mysql是程序名称 也可以/bin/bash或者bash)
    docker exec -it mysql mysql  -uroot -p123456  
    mysql> create database hmdp;# 创建黑马点评数据库
    mysql> use hmdp; # 使用黑马点评数据库
    
    # 3.登陆控制台执行source 命令,执行sql文件
    mysql> source hmdp.sql
    
  5. 进入Datagrip软件查看一下redis 和 mysql(我用的这个可视化软件,它可以连接很多种数据库)
    在这里插入图片描述

  6. 项目后端

    # 1.clone
    git clone https://gitee.com/chiroua/black-horse-review.git
    # 2.切换分支, init分支就是项目最开始的代码
    git checkout init
    # 3.修改application.yaml中mysql和redis的ip,端口,密码等,这里我redis没有设置密码直接注释了.
    
    • 因为使用的是mysql8的版本
      还要将数据配置文件里driver-class-name: com.mysql.jdbc.Driver修改为如下driver-class-name: com.mysql.cj.jdbc.Driver;否则启动项目有报错
    • 修改idea默认的maven, idea自带一个叫已捆绑(Maven3)的maven,默认的所在路径为/.m2,settings和repo都在这个目录下,但是我没试过,我这里用的自己的maven仓库,记得修改一下maven仓库镜像地址,搜索关键词 maven 阿里
      在这里插入图片描述
      在这里插入图片描述
  7. 项目前端

  • brew install nginx
  • /opt/homebrew/Cellar/nginx/1.25.3/下的html夹替换成src/main/resources/nginx-1.18.0/下的html文件夹
  • /opt/homebrew/etc/nginx/nginx.conf替换成src/main/resources/nginx-1.18.0/conf/nginx.conf
    接下来重新加载nginx配置文件sudo nginx -s reload
  • 启动nginxsudo nginx
  • ps -ef | grep nginx查看nginx是否成功启动
  1. nginx成功启动后,我们启动后端,访问本地8080端口即可成功运行

windows11 wsl2 ubuntu 环境配置

  1. mysql

    # 安装mysql
    su root
    apt update
    sudo apt-get install mysql-server
    sudo service mysql start#一般就直接启动了不需要这一步
    mysql
    ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password';# 修改root用户密码
    exit
    sudo mysql_secure_installation # 使用脚本删除匿名用户和匿名用户访问的数据库
    
    ## 接下来 执行sql文件,构建数据库
    cp /mnt/c/.....hmdp.sql /tmp/ #每个人不一样,可以参考我复制到/tmp目录下
    mysql -u root -p
    CREATE DATABASE hmdp;
    use hmdp;
    source /tmp/hmdp.sql
    

    更多操作查看此博客

  2. redis

    # 我直接安装的 没设置密码
    su root
    apt update
    sudo apt-get install redis-server
    # 执行redis-cli查看redis正常启动即可
    
  3. datagrip连接redis和mysql, 其中redis指定不需要用户和密码即可

  4. git clone git@gitee.com:chiroua/black-horse-review.git
    # 修改maven仓库的settings 将源设置成aliyun,这里我直接用idea自带的maven仓库了
    # 修改applicati.yml配置文件中mysql 和 redis相关配置
    

    因为使用的是mysql8的版本
    还要将数据配置文件里driver-class-name: com.mysql.jdbc.Driver修改为如下driver-class-name: com.mysql.cj.jdbc.Driver;否则启动项目有报错

  5. 前端也有一处问题 niginx目录下打开终端start nginx.exe后访问8080无效,
    查看nginx目录下的logs/error.log发现2024/01/04 21:58:41 [emerg] 37092#35300: CreateDirectory() "C:\Users\gx\Desktop\hmdp\black-horse-review\src\main\resources\nginx-1.18.0/temp/client_body_temp" failed (3: The system cannot find the path specified)
    我们手动在logs的同级目录下创建一个temp/client_body_temp即可

一.短信登录

1. 1发送验证码

  • 业务思路: 基本的校验->验证码和手机号保存在session中(不明白session token cookie去学),利用RandomUtil工具类生成验证码即可;session中存储的手机号和验证码是为了登录时的校验功能
@Override
    public Result sendCode(String phone, HttpSession session) {
        //获得用户手机号
        //1.校验手机号正确性
        if (RegexUtils.isPhoneInvalid(phone))
            return Result.fail("手机号格式不正确");
        session.setAttribute("phone", phone);
        //2.生成验证码
        String code = RandomUtil.randomNumbers(6);
        //3.保存验证码
        session.setAttribute("code", code);
        //4.发送验证码
        log.debug("发送短信验证码成功, 验证码:{}", code);
        //5.返回ok
        return Result.ok();
    }

1.2短信登录+注册

  • 业务思路:controller 中 return UserService接口中的方法, 具体实现在实现类UserServiceImpl中去重写即可;基本的参数校验->从session中取出验证码和手机号->判断手机号前后是否一致,判断验证码是否正确,判断用户是否存在,不存在则注册一个保存到数据库中
  • 说在前面: 本项目使用了mybatis-plus, 直接的好处是可以直接用save(), query等api, 原因是因为
    public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService
    我们观察发现 UserServiceImpl类继承了 ServiceImpl<T,T>, 同时指定了两个泛型UserMapper,和User,我们去entity下的user类可以发现,其中指明了该实体类对应的表名,这就是二者之间的联系.在这里插入图片描述
  • 代码优化:这里我优化了一下, 应当先检查当前手机号和session中存储的手机号是否一致
  • 注意代码命名规则,:比如session中的code命名为cacheCode,;类名:驼峰命名法+ 首字母大写, 方法名驼峰命名法+首字母小写
  • api设计规则:设计的类名称为IShopTypeService,对应的路径名称为shop-type
  • 写代码流程:先把思路捋清楚,比如1.2.3.4.每一步干什么,然后再写代码
@Override
	    public Result login(LoginFormDTO loginForm, HttpSession session) {
	        //1.校验手机号格式是否正确
	        String phone = loginForm.getPhone();
	        if(RegexUtils.isPhoneInvalid(phone)) return Result.fail("手机号格式不正确!");//这里不知道为啥,不用return true代表格式不正确
	        //2.校验手机号是否和session里面的一致
	        String cachePhone = (String) session.getAttribute("phone");
	        if (!cachePhone.equals(phone)) return Result.fail("前后手机号不匹配!");
	        //3.校验验证码
	        Object cacheCode = session.getAttribute("code");
	        String code = loginForm.getCode();
	        if (cacheCode == null || !cacheCode.toString().equals(code)) return Result.fail("验证码错误!");
	        //4.判断用户是否存在
	        User user = query().eq("phone", phone).one();
	        //5.不存在的话直接创建一个用户
	        if (user == null) user = createUserWithPhone(phone);
	        //6.保存用户信息到session中
	        session.setAttribute("user", user);
	        return Result.ok();
	    }
	//注册
	    private User createUserWithPhone(String phone) {
	        //1.创建用户
	        User user = new User();
	        user.setPhone(phone);
	        user.setNickName(USER_NICK_NAME_PREFIX + RandomUtil.randomString(10));
	        //2.保存用户到数据库中
	        save(user);
	        return user;
	    }

1.3登录校验拦截器

  • 业务思路:
    • 设计拦截器的原因: 点评的不少业务都需要验证当前是否是登录状态,比如我们观察前端info.html(个人信息界面)的代码发现会发送如下请求在这里插入图片描述
      其中有一个Cookie, 如果每个业务都要从http request的session中取的话会很麻烦,因此我们设置一个登录拦截器LoginInterceptor类, 所有的业务都会先经过这个类最后到达controller, 我们在这个类中将当前session中的user属性存储到本地线程中,要用的时候直接从本地线程UserHolder类(封装了TreadLocal类对象)中取就行.
    • 防止用户隐私泄露: 我们session中一开始存的是完整的user对象(包含了user的密码等个人隐私), 我们封装一个UserDto类, 将user中的信息拷贝到userDto中, session中存userDto就行, 后续的UserHolder等cache类从session中拿到的数据也就是userDto了. 但是我们createUser方法往数据库中存储的时候还是存储完整的user对象哦
    • 实现WebMvcConfigurer类中的addInterceptor方法, 并设置哪些请求路径不需要被拦截
  • 除了拦截器外还可以设计过滤器
  • 缺: 什么是TreadLocal内存泄露,为什么afterCompletion后要removeUser(); 什么是session cookie token
  • mac idea 实现方法快捷键 ^ + o
 @Override
    public Result me() {
        UserDTO user = UserHolder.getUser();
        return Result.ok(user);
    }
@Data
public class UserDTO {
    private Long id;
    private String nickName;
    private String icon;
}
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //1.获取session中的用户
        HttpSession session = request.getSession();
        Object user = session.getAttribute("user");
        //2.用户是否存在,不存在返回false,并设置状态码
        if (user == null){
            response.setStatus(401);
            return false;
        }
        //3.存在就将当前用户存储在ThreadLocal中
        UserHolder.saveUser((UserDTO) user);
        return true;
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //移除用户,防止内存泄漏
        UserHolder.removeUser();
    }
}
@Configuration
public class MyConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor()).excludePathPatterns(// 匿名对象 stream coding
                "/user/code",
                "/voucher/**",
                "/blog/hot",
                "/shop/**",
                "/shop-type/**",
                "/upload/**",
                "/user/login"
        );
    }
}

补缺Cookie Session Token

因为Http是一种无状态的协议,为了记住这种状态, 引入了 session cookie token

  • Cookie: 存储在前端, 数据的一种载体, 后端可以将数据包装在Cookie中发送给客户端端, 比如将session放在cookie中送到客户端, cookie跟随HTTP的每个请求发送出去
  • Session: 存储在后端
  • Token: 在很多地方都会用到, 只是一个通用的名词;诞生在服务器这端, 但保存在浏览器这边, 由客户端主导一切, 可以存放在Cookie或者Storage中, jwt即json web token是一种特殊的token,或者说token的一种
  • 总结: cookie是载体,而token和session则是靠它实现的验证机制,本质就是cookie上携带的字符串; session和token是两种认证机制。cookie和localstorage等是存储session id或token的载体

1.4基于redis+token认证实现短信登陆

  • 业务思路: 这里采用token认证机制, 不再使用session了, 因为如果是tomcat服务器集群,session存储在不同的tomcat服务器中, 假如小明的session存储在A服务器中, 第二次小明发送请求的时候经过负载均衡请求到达B服务器, 那么B服务器没有小明的session,小明又要重新登录; 但是如果让多个服务器之间数据同步的话会造成数据冗余,同时同步数据也需要一定的时间,综上我们选择redis来存储数据
    • 用redis存储code, phone作为其key
    • 用redis存储user信息, token作为其keytoken的本质是一种验证机制, 这里token作为当前用户个人信息的key!!
    • springboot内置的tomcat会帮我们维护session,这里我们自己维护redis的时候注意数据的丢失, 因为不可能让数据一直存储在redis中
    • 具体实现业务代码的时候注意自己new的对象spring不会帮我们管理的
  • 缺: 什么样的对象可以让spring帮我们管理?
 @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Override
    public Result sendCode(String phone, HttpSession session) {
        //获得用户手机号
        //1.校验手机号正确性
        if (RegexUtils.isPhoneInvalid(phone))
            return Result.fail("手机号格式不正确");
        //session.setAttribute("phone", phone); 使用redis后我还没想好它的key是啥
        //2.生成验证码
        String code = RandomUtil.randomNumbers(6);
        //3.保存验证码到redis中并设置缓存时间
        stringRedisTemplate.opsForValue().set(LOGIN_USER_KEY + phone, code, LOGIN_CODE_TTL, TimeUnit.MINUTES);//stream流
        //4.发送验证码
        log.debug("发送短信验证码成功, 验证码:{}", code);
        //5.返回ok
        return Result.ok();
    }

    @Override
    public Result login(LoginFormDTO loginForm, HttpSession session) {
        //1.校验手机号格式是否正确
        String phone = loginForm.getPhone();
        if(RegexUtils.isPhoneInvalid(phone)) return Result.fail("手机号格式不正确!");//这里不知道为啥,不用return true代表格式不正确
//        //2.校验手机号是否和session里面的一致
//        String cachePhone = (String) session.getAttribute("phone");
//        if (!cachePhone.equals(phone)) return Result.fail("前后手机号不匹配!");
        //3.校验验证码
        String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_USER_KEY + phone);
        String code = loginForm.getCode();
        if (cacheCode == null || !cacheCode.equals(code)) return Result.fail("验证码错误!");
        //4.判断用户是否存在
        User user = query().eq("phone", phone).one();
        //5.不存在的话直接创建一个用户
        if (user == null) user = createUserWithPhone(phone);
        //6.保存用户信息到redis中, token作为key并设置缓存时间
        String token = UUID.randomUUID().toString(true);
        String tokenKey = LOGIN_USER_KEY +  token;
        Map<String, Object> userMap = BeanUtil.beanToMap(user, new HashMap<>(),
                CopyOptions.create().setIgnoreNullValue(true)
                        .setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString()));
        stringRedisTemplate.opsForHash().putAll(tokenKey, userMap);
        //设置token有效期
        stringRedisTemplate.expire(tokenKey, LOGIN_USER_TTL, TimeUnit.MINUTES);
        //7.返回token
        return Result.ok(token);
    }
public class LoginInterceptor implements HandlerInterceptor {
    private StringRedisTemplate stringRedisTemplate;//用来接住外部传过来的StringRedisTemplate
    public LoginInterceptor(StringRedisTemplate stringRedisTemplate){
        this.stringRedisTemplate = stringRedisTemplate;
    }
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //1.获得token, 判断token是否为空
        String token = request.getHeader("authorization");
        if (StrUtil.isBlank(token)) return false;
        //2.通过token获取redis中的用户信息,并判断同户是否存在,不存在返回false,并设置状态码
        String tokenKey = LOGIN_USER_KEY + token;
        Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(tokenKey);
        if (userMap.isEmpty()){//及时为空也会被包装成一个对象,因此这里不能判断null而是isEmpty
            response.setStatus(401);
            return false;
        }
        //3.将map变成userDto
        UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);
        //4.存在就将当前用户存储在ThreadLocal中
        UserHolder.saveUser(userDTO);
        //5.每拦截一次, redis中存储的用户信息刷新一次
        stringRedisTemplate.expire(tokenKey, LOGIN_USER_TTL, TimeUnit.MINUTES);
        return true;
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //移除用户,防止内存泄漏
        UserHolder.removeUser();
    }
}
@Configuration
public class MyConfig implements WebMvcConfigurer {
    @Resource
    private StringRedisTemplate stringRedisTemplate;//该类中有一个@Configuration注解说明该类可以由spring来帮我们管理对象
    @Override
    public void addInterceptors(InterceptorRegistry registry) {//再该类中让spring帮我们管理对象然后传给LoginInterceptor
        registry.addInterceptor(new LoginInterceptor(stringRedisTemplate)).excludePathPatterns(// 匿名对象 stream coding
                "/user/code",
                "/voucher/**",
                "/blog/hot",
                "/shop/**",
                "/shop-type/**",
                "/upload/**",
                "/user/login"
        );
    }
}

1.5完善token认证的刷新机制

  • 业务思路: 因为之前的一层拦截器只能拦截部分请求, 假如用户一直在浏览index页面30min,那么再访问需要user信息的页面的时候就会需要重新登录, 因此这里再多设置一层拦截器拦截所有请求
    • 第一层拦截器: 拦截所有请求 + 将当前用户信息存储到ThreadLocal中;判断是否有用户的任务交给下一层,本层的任务是存储user到当前thread+刷新token(不判断token是否为空, 若无token代表无user信息, 无user信息的情况给第二层拦截器处理)
    • 第二层拦截器: 拦截部分需要user信息的页面的请求, 若从ThreadLocal中取出来的数据为空则直接return false 即可
    • 注意注册拦截器的时候拦截器有优先级顺序, 默认情况下都是0, 谁先注册谁先执行, 或者我们手动更改拦截器的优先级(优先级越小越先执行)
public class RefreshTokenInterceptor implements HandlerInterceptor {
    private StringRedisTemplate stringRedisTemplate;//用来接住外部传过来的StringRedisTemplate
    public RefreshTokenInterceptor(StringRedisTemplate stringRedisTemplate){
        this.stringRedisTemplate = stringRedisTemplate;
    }
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //1.获得token, 判断token是否为空
        String token = request.getHeader("authorization");
        if (StrUtil.isBlank(token)) {
            return true;//均return true, 判断是否有用户的任务交给下一层,本层的任务是存储user到当前thread+刷新token
        }
        //2.通过token获取redis中的用户信息,并判断同户是否存在,不存在返回false,并设置状态码
        String tokenKey = LOGIN_USER_KEY + token;
        Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(tokenKey);
        if (userMap.isEmpty()){//及时为空也会被包装成一个对象,因此这里不能判断null而是isEmpty
            return true;
        }
        //3.将map变成userDto
        UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);
        //4.存在就将当前用户存储在ThreadLocal中
        UserHolder.saveUser(userDTO);
        //5.每拦截一次, redis中存储的用户信息刷新一次
        stringRedisTemplate.expire(tokenKey, LOGIN_USER_TTL, TimeUnit.MINUTES);
        return true;
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //移除用户,防止内存泄漏
        UserHolder.removeUser();
    }
}
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //1.从UserHolder获得用户
        UserDTO userDto = UserHolder.getUser();
        //2.判断是否有user用户
        if (userDto == null) {
            response.setStatus(401);
            return false;
        }
        return true;
    }
    //不用重写afterCompletion方法, 第一层拦截器负责重写
    拦截器执行顺序:
    	preHandle: 拦截器1 -> 拦截器2
    	afterCompletion: 拦截器2 - > 拦截器1
}
@Configuration
public class MyConfig implements WebMvcConfigurer {
    @Resource
    private StringRedisTemplate stringRedisTemplate;//该类中有一个@Configuration注解说明该类可以由spring来帮我们管理对象
    @Override
    public void addInterceptors(InterceptorRegistry registry) {//再该类中让spring帮我们管理对象然后传给LoginInterceptor
        registry.addInterceptor(new LoginInterceptor()).excludePathPatterns(// 匿名对象 stream coding
                "/user/code",
                "/voucher/**",
                "/blog/hot",
                "/shop/**",
                "/shop-type/**",
                "/upload/**",
                "/user/login"
        ).order(1);
        registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate)).order(0);
    }
}

2.商户查询缓存

从本节开始我会说明这个业务的原因和业务的思路是如何的

2.1添加商户缓存(到redis中)

  • 业务原因
  • 业务思路
    查看网络请求时间验证是否快了很多

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Redis 给集合元素单独设置过期

其他系列文章导航 Java基础合集数据结构与算法合集 设计模式合集 多线程合集 分布式合集 ES合集 文章目录 其他系列文章导航 文章目录 前言 一、场景 1.1 消费队列 1.2 Redis实现 二、常见的方案 2.1 为单独的 field 设置过期 2.2 设置整体过期时间 2.3 zset 结合 sc…

项目实战:数字孪生可视化大屏幕,实现生产过程实时监控

项目介绍 智慧工厂数据可视化系统&#xff0c;融合工业大数据、物联网、人工智能等各类信息技术&#xff0c;整合厂区现有信息系统的数据资源&#xff0c;实现数字孪生工厂、设备运维监测、智能管网监测、综合安防监测、便捷通行监测、能效管理监测、生产管理监测、仓储物流监…

【数据分析实战】冰雪大世界携程景区评价信息情感分析采集词云

文章目录 引言数据采集数据集展示数据预处理 数据分析评价总体情况分析本人浅薄分析 各游客人群占比分析本人浅薄分析 各评分雷达图本人浅薄分析 差评词云-可视化本人浅薄分析 好评词云-可视化本人浅薄分析 综合分析写在最后 今年冬天&#xff0c;哈尔滨冰雪旅游"杀疯了&q…

技术学习|CDA level I 业务分析方法

业务分析方法有三个主要构成部分&#xff1a;业务指标分析、业务模型分析及业务分析方法。 业务指标分析是发现业务问题的核心方法&#xff1a;用于通用指标和场景指标的计算及分析方法&#xff0c;以及指标体系的设计与应用方法。业务模型是从一系列业务行为中抽象出来的信息…

请你列出逻辑电路中的24种表达式

随着时代发展&#xff0c;数字电路的使用频率越来越高&#xff0c;完全不低于模拟电路&#xff0c;因此从事数字电路的工程师越来越多&#xff0c;如果你想成为一名优秀的数字工程师&#xff0c;一定要学会下面的逻辑电路表达式&#xff01; 1、基本逻辑运算与运算 (AND): A AN…

04 帧 Frame

文章目录 04 帧 Frame4.1 相机相关信息4.2 特征点提取4.2.1 特征点提取 ExtractORB()4.3 ORB-SLAM2对双目/RGBD特征点的预处理4.3.1 双目视差公式4.3.2 双目图像特征点匹配 ComputeStereoMatches()4.3.3 根据深度信息构造虚拟右目图像&#xff1a;ComputeStereoFromRGBD() 4.4 …

Python中的h5py包使用

h5py是一个非常强大的工具&#xff0c;可以用于存储和处理大量科学数据。它可以帮助我们提高数据处理的效率和可靠性。 目录 一、h5py1.1 特点1.2 主要功能1.3 常用场景 二、安装h5py三、示例代码3.1 运行结果 四、总结 一、h5py h5py是Python中的一个库&#xff0c;提供了对H…

JS函数实现数字转中文大写

JS函数实现数字转中文大写 1. 数字转字符,分割,去除空字符2. 遍历分割字符,替换为中文3. 增加四位数单位4. 处理零5. 拼接四位数据和单位 项目中,JS将万亿以下正整数转为中文大写 1. 数字转字符,分割,去除空字符 function toChineseNumber(num){const strs num.toString().re…

计算机组成原理 总线

总线 总线定义 总线 总线是一组能为多个部件分时共享的公共信息传送线路 总线的好处 早期计算机外部设备少时大多采用分散连接方式&#xff0c;不易实现随时增减外部设备 为了更好地解决I/O设备和主机之间连接的灵活性问题&#xff0c;计算机的结构从分散连接发展为总线连接 …

Mac环境下Parallels Desktop 19的安装和使用

为了后续构建漏洞靶场和渗透测试环境&#xff0c;我们需要提前准备好几套与宿主机隔离的工作环境&#xff08;Windows、Linux等&#xff09;&#xff0c;在Mac上最常用的就是Paralles Desktop&#xff08;PD&#xff09;工具了&#xff0c;当前最新版本为19。接下来介绍如何安装…

QT工具栏开始,退出

QT工具栏开始&#xff0c;退出 //初始化场景QMenuBar *bar menuBar();setMenuBar(bar);QMenu *startbar bar->addMenu("开始");QAction * quitAction startbar->addAction("退出");connect(quitAction , &QAction::triggered,[](){this->c…

Chromedriver 下载和安装指南

1. 确定Chrome浏览器版本 首先&#xff0c;在谷歌浏览器中找到当前版本信息。 打开“设置”&#xff0c;点击“关于谷歌”即可看到版本号。确保后续下载的Chromedriver版本与Chrome浏览器版本一致。或者直接跳转网页地址&#xff1a;chrome://settings/help 2. 下载Chromedri…

js逆向第12例:猿人学第5题js混淆-乱码增强

文章目录 那么`RM4hZBv0dDon443M=`是怎么来的?密钥怎么找加密数组怎么破解_0x4e96b4[_$pr]m=,f=时间戳是哪个?打开控制台查看数据接口 https://match.yuanrenxue.cn/api/match/5?page=2&m=1704439385499&f=1704439384000 利用postman测试接口请求,判断参数是否强…

机器学习 - 决策树

场景 之前有说过k近邻算法&#xff0c;k近邻算法是根据寻找最相似特征的邻居来解决分类问题。k近邻算法存在的问题是&#xff1a;不支持自我纠错&#xff0c;无法呈现数据格式&#xff0c;且吃性能。k近邻算法的决策过程并不可视化。对缺失数据的样本处理很不友好&#xff0c;…

C++ OpenGL 3D GameTutorial 1:Making the window with win32 API学习笔记

视频地址https://www.youtube.com/watch?vjHcz22MDPeE&listPLv8DnRaQOs5-MR-zbP1QUdq5FL0FWqVzg 一、入口函数 首先看入口函数main代码&#xff1a; #include<OGL3D/Game/OGame.h>int main() {OGame game;game.Run();return 0; } 这里交代个关于C语法的问题&#x…

MidJourney笔记(10)-faq-fast-help-imagine-info-public-stealth

/faq 在官方 Midjourney Discord 服务器中使用可快速生成流行提示工艺频道常见问题解答的链接。 不过这个命令,我也是没有找到入口,之前还能在MidJourney的频道里使用,然后最近发现没有权限,有点奇怪。不知道系统又做了什么升级。 /fast 切换到快速模式。

七、HTML 文本格式化

一、HTML 文本格式化 加粗文本斜体文本电脑自动输出 这是 下标 和 上标 <!DOCTYPE html> <html><head><meta charset"utf-8"><title>HTML文本格式化</title> </head><body><b>加粗文本</b><br>…

HarmonyOS应用开发学习笔记 包名、icon图标,应用名修改 UIAbility组件介绍、UIAbility启动模式、UIAbility组件基本用法

目前HarmonyOS应用主推的是Stage模型开发 一、Stage模型基本概念 项目描述UIAbility组件UIAbility组件是一种包含UI界面的应用组件&#xff0c;主要用于和用户交互。例如&#xff0c;图库类应用可以在UIAbility组件中展示图片瀑布流&#xff0c;在用户选择某个图片后&#xf…

Mysqld的关键优化参数

skip-name-resolve 现象 mysql连接很慢&#xff0c;登陆到服务器上查看服务器日志都是正常的&#xff0c;无可疑记录&#xff0c;登陆到mysql服务器上&#xff0c;查看下进程&#xff0c;发现有很多这样的连接&#xff1a; 218 | unauthenticated user | 192.168.10.6:44500 |…

Stable Diffusion架构的3D分子生成模型 GeoLDM - 测评与代码解析

之前&#xff0c;向大家介绍过3D分子生成模型 GeoLDM。 GeoLDM按照Stable Diffusion架构&#xff0c;将3D分子生成的扩散过程运行在隐空间内&#xff0c;优化了基于扩散模型的分子生成。可能是打开Drug-AIGC的关键之作。让精确控制分子生成有了希望。 详见&#xff1a;分子生成…