用户中心(下)

计划

  1. 开发完成后端登录功能 (单机登录 => 后续改造为分布式 / 第三方登录)✔
  2. 开发后端用户的管理接口 (用户的查询 / 状态更改)✔
  3. 后端接口测试 ✔
  4. 开发前端用户登录注册功能
  5. 讨论如何校验用户(星球的小伙伴可以使用)

登录逻辑

接口

接受参数:用户账户、密码
请求类型:POST
请求体:JSON 格式的数据

请求参数很长,或者无法预料的情况,不建议用 GET

返回值:用户信息(脱敏)

逻辑

  1. 校验用户和密码是否合法
    1. 非空
    2. 账户长度不小于 4 位
    3. 密码长度不小于 8 位
    4. 账户不包含特殊字符
  2. 校验密码是否输入正确,要和数据库中的密文密码对比
  3. 用户信息脱敏,隐藏敏感信息,防止数据库中的字段泄露
  4. 记录用户的登录态(我们用 Session),将其存到服务器上(用后端 SpringBoot 框架封装的服务器 Tomcat 记录即可)

如何知道是哪个用户登录了?
1连接上服务器后,得到一个 session 状态(匿名会话),返回给前端
2登录成功后,得到了登录成功的 session,并且给该 session 设置一些值(比如用户信息),返回给前端一个设置 cookie 的“命令”
3前端接收到后端的命令后,设置 cookie,保存到浏览器内
4前端再次请求后端的时候(相同的域名),在请求头中带上 cookie 去请求
5后端拿到前端传来的 cookie,找到对应的 session
6后端从 session 中可以取出基于该 session 存储的变量(用户的登录信息、登录名)

  1. 返回用户信息(脱敏后的)
简单说明cookie和session

首先,cookie是一种缓存机制,session是会话机制
:::info
🪔以最常见的登陆案例讲解cookie的使用过程:
(1)首先用户在客户端浏览器向服务器首次发起登陆请求
(2)登陆成功后,服务端会把登陆的用户信息设置在cookie 中,并将cookie返回给客户端浏览器
(3)客户端浏览器接收到 cookie 请求后,会把 cookie 保存到本地(可能是内存,也可能是磁盘,看具体使用情况而定)
(4)以后再次访问该 web 应用时,客户端浏览器就会把本地的 cookie 带上,这样服务端就能根据 cookie 获得用户信息了
:::

🪔同样以登陆案例为例子讲解 session 的使用过程:
(1)首先用户在客户端浏览器发起登陆请求
(2)登陆成功后,服务端会把用户信息保存在服务端,并返回一个唯一的 session 标识给客户端浏览器。
(3)客户端浏览器会把这个唯一的 session 标识保存在起来
(4)以后再次访问 web 应用时,客户端浏览器会把这个唯一的 session 标识带上,这样服务端就能根据这个唯一标识找到用户信息。

看到这里可能会引起疑问:把唯一的 session 标识返回给客户端浏览器,然后保存起来,以后访问时带上,这难道不是 cookie 吗?

没错,session 只是一种会话机制,在许多 web 应用中,session 机制就是通
过 cookie 来实现的。也就是说它只是使用了 cookie 的功能,并不是使用 cookie
完成会话保存。与 cookie 在保存客户端保存会话的机制相反,session 通过 cookie
的功能把会话信息保存到了服务端。

session和cookie有什么区别?

(1)cookie 是浏览器提供的一种缓存机制,它可以用于维持客户端与服务端之间的会话
(2)session 指的是维持客户端与服务端会话的一种机制,它可以通过 cookie 实现,也可以
通过别的手段实现。
(3)如果用 cookie 实现会话,那么会话会保存在客户端浏览器中
(4)而 session 机制提供的会话是保存在服务端的。

🦥举个小例子说明Cookie和Session之间的区别和联系
假如一个咖啡店有喝五杯赠一杯咖啡的优惠,但是一次性消费5杯咖啡的客人很少,这时就需要某种方式来记录某位顾客的消费数量。无外乎下面的几种方案:
1、该店的店员很厉害,能记住每位顾客的消费数量,只要顾客一走进咖啡店,店员就知道该怎么对待了。这种做法就是协议本身支持状态。但是http协议本身是无状态的。
2、发给顾客一张卡片,上面记录着消费的数量,一般还有个有效期限。每次消费时,如果顾客出示这张卡片,则此次消费就会与以前或以后的消费相联系起来。这种做法就是在客户端保持状态,也就是cookie,顾客就相当于浏览器。
3、发给顾客一张会员卡,除了卡号之外什么信息也不纪录,每次消费时,如果顾客出示该卡片,则店员在店里的记录本上找到这个卡号对应的记录添加一些消费信息。这种做法就是在服务器端保持状态。

源于星球炎大佬,解释的很清晰

写代码流程

先做设计
代码实现
持续优化!!!(代码复用性,提取公共逻辑/常量)

后端
逻辑层

UserService

public interface UserService extends IService<User> {/***用户注册* @param userAccount 用户账户* @param userPassword 用户密码* @param checkPassword 校验密码* @return 用户id*/long userRegister(String userAccount, String userPassword, String checkPassword);/*** 用户登录* @param userAccount 用户账户* @param userPassword 用户密码* @param request 请求* @return 脱敏后的用户信息*/User userLogin(String userAccount, String userPassword, HttpServletRequest request);
}

UserServiceImpl

@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>implements UserService{@Autowiredprivate UserMapper userMapper;/*** 盐值, 混淆密码*/private static final String SALT = "yupi";private static final String USER_LOGIN_STATE = "userLoginState";@Overridepublic long userRegister(String userAccount, String userPassword, String checkPassword) {//1.校验if(StringUtils.isAnyBlank(userAccount, userPassword, checkPassword)){//TO doreturn  -1;}//账户不小于4位if(userAccount.length() < 4){return -1;}//密码不小于 8 位if(userPassword.length() < 8){return  -1;}//账户不包含特殊字符String validPattern = "[`~!@#$%^&*()+=|{}':;',\\\\\\\\[\\\\\\\\].<>/?~!@#¥%……&*()——+|{}【】‘;:”“’。,、?\\s]";Matcher matcher = Pattern.compile(validPattern).matcher(userAccount);if(matcher.find()){return -1;}//校验密码和密码相同if(!userPassword.equals(checkPassword)){return -1;}//账户不能重复QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.eq("userAccount", userAccount);long count = userMapper.selectCount(queryWrapper);if(count > 0){return -1;}//2.加密String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());//3.插入数据User user = new User();user.setUserAccount(userAccount);user.setUserPassword(encryptPassword);boolean saveResult = this.save(user);if (!saveResult){return -1;}return user.getId();}@Overridepublic User userLogin(String userAccount, String userPassword, HttpServletRequest request) {//1.校验if(StringUtils.isAnyBlank(userAccount, userPassword)){//TO doreturn  null;}//账户不小于4位if(userAccount.length() < 4){return null;}//密码不小于 8 位if(userPassword.length() < 8){return  null;}//账户不包含特殊字符String validPattern = "[`~!@#$%^&*()+=|{}':;',\\\\\\\\[\\\\\\\\].<>/?~!@#¥%……&*()——+|{}【】‘;:”“’。,、?\\s]";Matcher matcher = Pattern.compile(validPattern).matcher(userAccount);if(matcher.find()){return null;}//2.加密String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());//查询用户是否存在QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.eq("userAccount", userAccount);queryWrapper.eq("userPassword", encryptPassword);User user = userMapper.selectOne(queryWrapper);// 用户不存在if(user == null) {log.info("user login failed, userAccount cannot match userPassword");return null;}//3. 用户脱敏User safetyUser = new User();safetyUser.setId(user.getId());safetyUser.setUsername(user.getUsername());safetyUser.setUserAccount(user.getUserAccount());safetyUser.setAvatarUrl(user.getAvatarUrl());safetyUser.setGender(user.getGender());safetyUser.setEmail(user.getEmail());safetyUser.setUserStatus(user.getUserStatus());safetyUser.setPhone(user.getPhone());safetyUser.setCreateTime(user.getCreateTime());//4. 记录用户的登录态request.getSession().setAttribute(USER_LOGIN_STATE, safetyUser);return safetyUser;}
}

逻辑删除,MyBatis-Plus 能自动实现逻辑删除,Mybatis-Plus 逻辑删除

mybatis-plus:global-config:db-config:logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)logic-delete-value: 1 # 逻辑已删除值(默认为 1)logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)

这里的 flag 我们是 isDelete

还要在 User 这个类中找到这个属性,然后我们加上注解 @TableLogic

/*** 是否删除*/
@TableLogic
private Integer isDelete;
控制层

创建 UserController
记得加上注解 @RestController,适用于编写 Restful 风格的 API,返回值默认为 JSON 类型
还有 @RequestMapping 请求路径

@RestController
@RequestMapping("/user")
public class UserController {@Resourceprivate UserService userService;@PostMapping("/register")public Long userRegister(@RequestBody UserRegisterRequest userRegisterRequest) {if (userRegisterRequest == null){return null;}String userAccount = userRegisterRequest.getUserAccount();String userPassword = userRegisterRequest.getUserPassword();String checkPassword = userRegisterRequest.getCheckPassword();if(StringUtils.isAnyBlank(userAccount,userPassword,checkPassword)) {return null;}return userService.userRegister(userAccount, userPassword, checkPassword);}@PostMapping("/login")public User userLogin(@RequestBody UserLoginrequest userLoginrequest, HttpServletRequest request) {if (userLoginrequest == null){return null;}String userAccount = userLoginrequest.getUserAccount();String userPassword = userLoginrequest.getUserPassword();if(StringUtils.isAnyBlank(userAccount,userPassword)) {return null;}return userService.userLogin(userAccount, userPassword, request);}
}

我们这里需要建立封装一个请求对象,接收请求
在 model 包下建立 request 包,新增对象 UserRegisterRequest、UserLoginRequest(这里列举一个)
这里实现序列化接口(具体详细可以百度)应用较为广泛

private static final long serialVersionUID = -2406654025944442247L; 是一个序列化版本号,用于标识序列化类的版本。当类的结构发生变化时,如果没有显式指定 serialVersionUID,Java 编译器会自动生成一个版本号,如果类的结构发生了变化,反序列化时可能会导致版本不一致的问题。因此,显式地指定 serialVersionUID 可以确保在类结构发生变化时,版本号保持一致,从而避免反序列化时的问题。

@Data
public class UserRegisterRequest implements Serializable {private static final long serialVersionUID = -2406654025944442247L;private String userAccount;private String userPassword;private String checkPassword;
}

为什么在逻辑层已经做过参数校验这里控制层也要做呢?
控制层倾向于对请求参数本身的校验,不涉及业务逻辑本身
而逻辑层是对业务逻辑的校验,有可能被除了控制层以外的类调用

测试

这里可以用IDEA自带的接口测试,也可以借助其他软件Postman等
image.png
或者直接点击这个按钮也可以
image.png

POST http://localhost:8080/user/login
Content-Type: application/json{"userAccount": "yupi","userPassword": 12345678
}

可以debug 模式感受session 登录态的记录,以及数据的传递

用户管理接口

必须要鉴权!

  1. 查询用户
    1. 允许根据用户名查询
  2. 删除用户

这里偷懒了,直接在控制层写了,但其实合理点的话应该在逻辑层写
较为简单的查询等操作可以直接在控制层写(不规范)

@GetMapping("/search")public List<User> searchUsers(String username, HttpServletRequest request) {if (!isAdmin(request)) {return new ArrayList<>();}QueryWrapper<User> queryWrapper = new QueryWrapper<>();if (StringUtils.isNotBlank(username)) {queryWrapper.like("username", username);}List<User> userList = userService.list();return userList.stream().map(user -> userService.getSafetyUser(user)).collect(Collectors.toList());}@PostMapping("/delete")public boolean deleteUser(@RequestBody Long id , HttpServletRequest request) {if (!isAdmin(request)) {return false;}if(id <= 0) {return  false;}return userService.removeById(id);}

这里将相同的代码逻辑摘出来(即鉴权)

private boolean isAdmin(HttpServletRequest request) {// 鉴权,只有管理员可以查询Object userObj = request.getSession().getAttribute(USER_LOGIN_STATE);User user = (User) userObj;if(user == null || user.getUserRole() != ADMIN_ROLE) {return false;}return  true;}

注意:只是这么写还不行,因为这么写的话,什么人都能够调用,就很危险了
而我们此时的 user 表里面没有管理员这个字段,因此我们加一个

/*** 用户角色 0-普通用户 1-管理员*/
private Integer role;

注意其他相关地方都要改, 数据库相关的可以用MybatisX插件重写

由于常量过多调用,这里定义用户常量类

public interface UserConstant {//用户登录态键String USER_LOGIN_STATE = "userLoginState";/*** 用户权限*/int DEFAULT_ROLE = 0;//普通用户,默认权限int ADMIN_ROLE = 1;//管理员
}

定义session 失效时间,减少记录登录态,方便测试

spring:application:name: user-centerdatasource:# 驱动类名称driver-class-name: com.mysql.cj.jdbc.Driver# 数据库连接的urlurl: jdbc:mysql://localhost:3306/ania# 连接数据库的用户名username: root# 连接数据库的密码password: rootservlet:multipart:max-file-size: 10MBmax-request-size: 100MB
# session 失效时间session:timeout: 86400
server:port: 8080mybatis-plus:configuration:map-underscore-to-camel-case: falseglobal-config:db-config:logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)logic-delete-value: 1 # 逻辑已删除值(默认为 1)logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)

为了避免用户密码被返回, 过滤优化代码
UserService

/*** 用户脱敏* @param originUser 起始用户* @return 安全用户*/User getSafetyUser(User originUser);

UserServiceImpl

/*** 用户脱敏* @param originUser 起始用户* @return 安全用户*/@Overridepublic User getSafetyUser(User originUser) {User safetyUser = new User();safetyUser.setId(originUser.getId());safetyUser.setUsername(originUser.getUsername());safetyUser.setUserAccount(originUser.getUserAccount());safetyUser.setAvatarUrl(originUser.getAvatarUrl());safetyUser.setGender(originUser.getGender());safetyUser.setEmail(originUser.getEmail());safetyUser.setUserRole(originUser.getUserRole());safetyUser.setUserStatus(originUser.getUserStatus());safetyUser.setPhone(originUser.getPhone());safetyUser.setCreateTime(originUser.getCreateTime());return safetyUser;}

UserController

@GetMapping("/search")public List<User> searchUsers(String username, HttpServletRequest request) {if (!isAdmin(request)) {return new ArrayList<>();}QueryWrapper<User> queryWrapper = new QueryWrapper<>();if (StringUtils.isNotBlank(username)) {queryWrapper.like("username", username);}List<User> userList = userService.list();// 调用getSafetyUser方法,得到脱敏后的User也实现了返回结果过滤return userList.stream().map(user -> userService.getSafetyUser(user)).collect(Collectors.toList());}
前端
简化代码

image.png
页脚部分·
image.png
修改页脚链接,title等,换成想要的
image.png

标题,logo部分
image.png
删除不用功能(代码)
image.png
image.png
修改显示内容
image.png
增添密码长度提示
image.png
定义全局常量包constants,便于使用常量(如logo的链接,编程导航链接等)

export const SYSTEM_LOGO = "data:image/png;base64,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";export const PLANTE_LINK = "https://wx.zsxq.com/dweb2/index/group/51122858222824"

image.png
精简后页面
image.png

对接后端

调整参数名称一致,便于进行类型检查和参数传递
image.png
这里可以Ctrl + r 全局修改
image.png

这里改成user,如果user存在的话,就显示登录成功,并且设置用户的登录状态为user

image.png
修改接口
image.png

这里返回结果不太一样,后面会再调
前端需要向后端发送请求
前端用 ajax 来请求后端,axios 封装了 ajax,request 是 ant design 项目又封装了一次
追踪 request 源码:用到了 umi 插件,requestConfig 是一个配置

image.png
按照官网的提示,我们修改 request 后面的请求地址,还有修改一下配置Ant Design Pro的官方文档,搜索请求

import { RequestConfig } from 'umi';export const request: RequestConfig = {timeout: 1000,errorConfig: {},middlewares: [],requestInterceptors: [],responseInterceptors: [],errorHandler,
};

image.png
此时访问的请求地址就是
image.png
但此时后端是用 8080,我们前端使用 8000,这样就对不上,要么跨域,要么代理,后者比较简便一些

代理

正向代理:替客户端向服务器发送请求
反向代理:替服务器接收请求
怎么弄?
通过 Nginx 服务器、Node.js 服务器

image.png
后端 application.yml 指定接口全局 api
image.png
修改超时时间为10s
image.png
debug启动UserCenterApplication
后端成功拿到数据
image.png
前端也有相关响应
image.png

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

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

相关文章

基于EO平衡优化器算法的目标函数最优值求解matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 基于EO平衡优化器算法的目标函数最优值求解matlab仿真。提供九个测试函数&#xff0c;分别对九个测试函数仿真输出最优解以及对应的优化收敛曲线。 2.测试软件版…

树莓派点亮LED灯

简介 使用GPIO Zero library 的 Python库实现点亮LED灯。接线 树莓派引脚参考图如下&#xff1a; LED正极 接GPIO17 LED负极 接GND 权限 将你的用户加到gpio组中&#xff0c; 否则无法控制GPIO sudo usermod -a -G gpio 代码 from gpiozero import LED from time impor…

ES全文检索支持拼音和繁简检索

ES全文检索支持拼音和繁简检索 1. 实现目标2. 引入pinyin插件2.1 编译 elasticsearch-analysis-pinyin 插件2.2 安装拼音插件 3. 引入ik分词器插件3.1 已有作者编译后的包文件3.2 只有源代码的版本3.3 安装ik分词插件 4. 建立es索引5.测试检索6. 繁简转换 1. 实现目标 ES检索时…

Springboot+Vue+小程序+基于微信小程序护农远程看护系统

开发平台为idea&#xff0c;maven管理工具&#xff0c;Mybatis操作数据库&#xff0c;根据市场数字化需要为农户打造小程序可远程查看农场的种植情况。项目是调试&#xff0c;讲解服务均可有偿获取&#xff0c;需要可在最下方QQ二维码处联系我。 SpringbootVue小程序&#xff…

【UE5】数字人基础

这里主要记录一下自己在实现数字人得过程中涉及导XSens惯性动捕&#xff0c;视频动捕&#xff0c;LiveLinkFace表捕&#xff0c;GRoom物理头发等。 一、导入骨骼网格体 骨骼网格体即模型要在模型雕刻阶段就要雕刻好表捕所需的表情体(blendshape)&#xff0c;后面表捕的效果直…

API安全

一&#xff0c;什么是API API指的是应用程序编程接口&#xff08;Application Programming Interface&#xff09;&#xff0c;是一组定义了软件组件如何相互交互的规范。通过API&#xff0c;不同的软件可以相互通信和交换数据&#xff0c;实现不同软件之间的集成和互操作。 …

代码随想录算法训练营DAY45|C++动态规划Part7|70.爬楼梯(进阶版)、322. 零钱兑换、279.完全平方数

文章目录 70.爬楼梯&#xff08;进阶版&#xff09;322. 零钱兑换思路CPP代码 279.完全平方数思路CPP代码 70.爬楼梯&#xff08;进阶版&#xff09; 卡码网&#xff1a;57. 爬楼梯 文章讲解&#xff1a;70.爬楼梯(进阶版) 322. 零钱兑换 力扣题目链接 文章讲解&#xff1a;322…

llama_index微调BGE模型

微调模型是为了让模型在特殊领域表现良好,帮助其学习到专业术语等。 本文采用llama_index框架微调BGE模型,跑通整个流程,并学习模型微调的方法。 一、环境准备 Linux环境,GPU L20 48G,Python3.8.10。 pip该库即可。 二、数据准备 该框架实现了读取各种类型的文件,给…

LNMP部署及应用(Linux+Nginx+MySQL+PHP)

LNMP 我们为什么采用LNMP这种架构? 采用Linux、PHP、MySQL的优点我们不必多说。 Nginx是一个小巧而高效的Linux下的Web服务器软件&#xff0c;是由 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发的&#xff0c;已经在一些俄罗斯的大型网站上运行多年&#xff0c;目…

数据分析及AI技术在旅游行业的应用

引言 旅游行业是一个充满潜力和机遇的领域&#xff0c;而数据分析和人工智能&#xff08;AI&#xff09;技术的迅猛发展为这个行业带来了前所未有的机遇和挑战。本文将探讨数据分析及AI技术在旅游行业中的具体应用及其带来的影响。 数据分析在旅游行业的4种应用 在旅游行业…

windows编程中的位图操作,界面中插入位图方法

在界面中插入位图&#xff0c;主要用到BitBlt这个函数 BOOL CDC::BitBlt(int x, int y, int width, int height, CDC *src, int src_x, int src_y, DWORD dwRop)用法说明见官方说明 这里要注意的是&#xff0c;这个函数是把源图像从src这个设备上下文传递到目标设备上下文(这…

如何从 iPhone 恢复已删除或丢失的联系人?

不小心删除了您的 iPhone 联系人&#xff1f;不用担心。我们将向您展示如何从 iPhone或 iPad恢复已删除或丢失的联系人。当您从 iPhone 中删除联系人时&#xff0c;您可能认为无法将其恢复。但事实是&#xff0c;您可以从 iPhone 或 iPad 恢复已删除的联系人&#xff0c;因为它…

链表经典练习题

目录 前言&#xff1a; 一、反转单链表 二、链表的中间结点 三、合并两个有序链表 四、分割链表 五、约瑟夫问题 六、判断链表是否有环&#xff1f; 七、求环形链表的入口点 八、输入一个链表&#xff0c;输出该链表中倒数第k个结点 九、输入两个链表&#xff0c;找出…

云原生Kubernetes: K8S 1.29版本 部署Sonarqube

一、实验 1.环境 &#xff08;1&#xff09;主机 表1 主机 主机架构版本IP备注masterK8S master节点1.29.0192.168.204.8 node1K8S node节点1.29.0192.168.204.9node2K8S node节点1.29.0192.168.204.10已部署Kuboard &#xff08;2&#xff09;master节点查看集群 1&…

开箱子咸鱼之王H5游戏源码_内购修复优化_附带APK完美运营无bug最终版__GM总运营后台_附带安卓版本

内容目录 一、详细介绍二、效果展示2.效果图展示 三、学习资料下载 一、详细介绍 1.包括原生打包APK&#xff0c;资源全部APK本地化&#xff0c;基本上不跑服务器宽带 2.优化后端&#xff0c;基本上不再一直跑内存&#xff0c;不炸服响应快&#xff01; 3.优化前端&#xff0c…

WSL2-Ubuntu使用Conda配置百度飞浆paddlepaddle虚拟环境

0x00 缘起 本文将介绍在WSL2-Ubuntu系统中,使用Conda配置百度飞浆paddlepaddle虚拟环境中所出现的各种问题以及解决方法,最终运行"run_check()"通过测试。 在WSL2中配置paddlepaddle不像配置Pytorch那样顺滑,会出现各种问题(如:库的文件缺失、不知道如何匹配C…

Web后端开发中对三层架构解耦之控制反转与依赖注入

内聚与耦合 内聚 比如说我们刚刚书写的员工的实现类 在这里我们仅仅书写的是和员工相关的代码 而与员工无关的代码都没有放到这里 说明内聚程度较高 耦合 以后软件开发要高内聚 低耦合 提高程序灵活性 扩拓展性 分析代码 如何解耦 创建容器 提供一个容器 存储东西 存储E…

STM32 F103C8T6学习笔记16:1.3寸OLED的驱动显示日历

今天尝试使用STM32 F103C8T6驱动显示 1.3寸的OLED&#xff0c;显示数字、字符串、汉字、图片等 本质与0.96寸的OLED是完全相同的原理&#xff1a; 而且经过我的研究发现: 1.3寸大小的OLED并未比0.96寸的有更多的显示像素点数来显示&#xff0c;也是128*64的像素点数显示: 也…

力扣153. 寻找旋转排序数组中的最小值

Problem: 153. 寻找旋转排序数组中的最小值 文章目录 题目描述思路复杂度Code 题目描述 思路 1.初始化左右指针left和right&#xff0c;指向数组的头和尾&#xff1b; 2.开始二分查找&#xff1a; 2.1.定义退出条件&#xff1a;当left right时退出循环&#xff1b; 2.2.当nums…

Python爬虫-BeautifulSoup解析

1.简介 BeautifulSoup 是一个用于解析 HTML 和 XML 文档的 Python 库。它提供了一种灵活且方便的方式来导航、搜索和修改树结构或标记文档。这个库非常适合网页抓取和数据提取任务&#xff0c;因为它允许你以非常直观的方式查询和操作文档内容。 2.安装 Beautiful Soup 终端输…