当前位置: 首页 > news >正文

用户中心(下)

计划

  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

相关文章:

用户中心(下)

文章目录 计划登录逻辑接口简单说明cookie和session写代码流程后端逻辑层控制层测试用户管理接口 前端简化代码对接后端代理 计划 开发完成后端登录功能 &#xff08;单机登录 > 后续改造为分布式 / 第三方登录&#xff09;✔开发后端用户的管理接口 &#xff08;用户的查询…...

商务分析方法与工具(六):Python的趣味快捷-字符串巧妙破解密码本、身份证号码、词云图问题

Tips&#xff1a;"分享是快乐的源泉&#x1f4a7;&#xff0c;在我的博客里&#xff0c;不仅有知识的海洋&#x1f30a;&#xff0c;还有满满的正能量加持&#x1f4aa;&#xff0c;快来和我一起分享这份快乐吧&#x1f60a;&#xff01; 喜欢我的博客的话&#xff0c;记得…...

ftp方式和http方式搭建云仓库

1.搭建阿里云仓库 国外云仓库比较慢&#xff0c;可以使用阿里云仓库代替 1.服务端和客户端切换到 yum.repo.d 目录 将自带的仓库移走 [rootlocalhost ~] cd /etc/yum.repos.d/ [rootlocalhost yum.repos.d] mkdir bak [rootlocalhost yum.repos.d] mv *.repo bak/ [rootloca…...

vue2 + antvx6 实现流程图功能

导入关键包 npm install antv/x6 --save npm install antv/x6-vue-shape 保存插件 (可选) npm install --save antv/x6-plugin-clipboard antv/x6-plugin-history antv/x6-plugin-keyboard antv/x6-plugin-selection antv/x6-plugin-snapline antv/x6-plugin-stencil antv/…...

IDEA 中的奇技淫巧

IDEA 中的奇技淫巧 书签 在使用ctrlalt方向键跳转时&#xff0c;或者追踪代码时&#xff0c;经常遇到的情况是层级太多&#xff0c;找不到代码的初始位置&#xff0c;入口。可以通过书签的形式去打上一个标记&#xff0c;后续可以直接跳转到书签位置。 标记书签&#xff1a;c…...

LSTM-KDE的长短期记忆神经网络结合核密度估计多变量回归区间预测(Matlab)

LSTM-KDE的长短期记忆神经网络结合核密度估计多变量回归区间预测&#xff08;Matlab&#xff09; 目录 LSTM-KDE的长短期记忆神经网络结合核密度估计多变量回归区间预测&#xff08;Matlab&#xff09;效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.LSTM-KDE的长短期…...

CMakeLists.txt语法规则:部分常用命令说明三

一. 简介 前面几篇文章学习了CMakeLists.txt语法中 add_executable命令&#xff0c;add_library命令&#xff0c;aux_source_directory命令&#xff0c;include_directories命令&#xff0c;add_subdirectory 命令的简单使用。文章如下&#xff1a; CMakeLists.txt语法规则&…...

android init进程启动流程

一,Android系统完整的启动流程 二,android 系统架构图 三,init进程的启动流程 四,init进程启动服务的顺序 五,android系统启动架构图 六,Android系统运行时架构图 bool Service::Start() {// Starting a service removes it from the disabled or reset state and// imme…...

利用爬虫解决数据采集难题

文章目录 安装为什么选择 BeautifulSoup 和 requests&#xff1f;安装 BeautifulSoup 和 requests解决安装问题 示例总结 在现代信息时代&#xff0c;数据是企业决策和发展的关键。然而&#xff0c;许多有用的数据分散在网络上&#xff0c;且以各种格式和结构存在&#xff0c;因…...

智慧粮库/粮仓视频监管系统:AI视频智能监测保障储粮安全

智慧粮库视频监管系统是一种基于物联网、AI技术和视频监控技术的先进管理系统&#xff0c;主要用于对粮食储存环境进行实时监测、数据分析和预警。TSINGSEE青犀智慧粮库/粮仓视频智能管理系统方案通过部署多区域温、湿度、空气成分等多类传感器以及视频监控等设施&#xff0c;对…...

经验浅谈!伦敦银如何交易?

近期&#xff0c;伦敦银价格出现很强的上涨&#xff0c;这促使一些新手投资者进入了市场&#xff0c;但由于缺乏经验&#xff0c;他们不知道该怎么在市场中交易&#xff0c;下面我们就从宏观上介绍一些方法&#xff0c;来讨论一下伦敦银如何交易。 首先我们要知道&#xff0c;要…...

信息系统项目管理师(高项)_习题杂记

1.GB/T16260-2006《软件工程产品质量》系列标准&#xff1a; 1&#xff09;GB/T16260.1-2006《软件工程产品质量第1部分&#xff1a;质量模型》&#xff0c;提出了软件生存周期中的质量模型&#xff1b; 2&#xff09;GB/T16260.2-2006《软件工程产品质量第2部分&#xff1a;…...

CMakeLists.txt 简单的语法介绍

一. 简介 前面通过几个简单地示例向大家演示了 cmake 的使用方法&#xff0c;由此可知&#xff0c;cmake 的使用方法其实还是非常简单的&#xff0c;重点在于编写 CMakeLists.txt&#xff0c;CMakeLists.txt 的语法规则也简单&#xff0c;并没有 Makefile 的语法规则那么复杂难…...

AI时代:人工智能大模型引领科技创造新时代

目录 前言一. AI在国家战略中有着举足轻重的地位1.1 战略1.2 能源1.3 教育 二. AI在日常生活中扮演着重要角色2.1 医疗保健2.2 智能客服2.3 自动驾驶2.4 娱乐和媒体2.5 智能家居 三. AI的未来发展趋势 总结 前言 随着AI技术的进步&#xff0c;新一代的AI技术已经开始尝试摆脱依…...

为什么 IP 地址通常以 192.168 开头?(精简版)

网络通讯的本质就是收发数据包。如果说收发数据包就跟收发快递一样。IP地址就类似于快递上填的收件地址和发件地址一样&#xff0c;路由器就充当快递员的角色&#xff0c;在这个纷繁复杂的网络世界里找到该由谁来接收这个数据包&#xff0c;所以说&#xff1a;IP地址就像快递里…...

【HEC】HECRAS中的降雨边界

目录 说明HEC-RAS网格降雨模型与HEC-HMS的比较HECRAS 降雨边界2D Area降雨边界添加降水边界条件调整2D Flow Area特性添加入渗网格数据创建土地覆盖层创建土壤层创建入渗层指定几何图形关联具有空间变化的网格降水数据Point点数据Gridded网格化数据Constant恒定值蒸散和风数据...

搜索算法系列之三(插值查找)

前言 插值查找仅适用于有序数据、有序数组&#xff0c;和二分查找类似&#xff0c;更讲究数据有序均匀分布。 算法原理 插值查找(interpolation search)是一种查找算法&#xff0c;它与二分查找类似&#xff0c;但在寻找元素时更加智能化。这种算法假设数据集是等距的或者有…...

前端奇怪面试题总结

面试题总结 不修改下面的代码进行正常解构 这道题考的是迭代器和生成器的概念 let [a,b] {a:1,b:2}答案 对象缺少迭代器&#xff0c;需要手动加上 Object.prototype[Symbol.iterator] function* (){// return Object.values(this)[Symbol.iterator]()return yeild* Object.v…...

NPM--最新淘宝镜像源地址

最新淘宝镜像源地址&#xff1a; 原来的 https://registry.npm.taobao.org 已替换为 https://registry.npmmirror.com 查看镜像源 npm config get registry 更换为淘宝最新镜像源 npm config set registry https://registry.npmmirror.com...

vue3中实现地区下拉选择组件封装

1组件文件 新建一个文件夹内&#xff0c;包含inde.vue,index.ts,pac.json这三个文件 index.vue文件 <template><el-cascaderv-model"data":options"pcaData":style"{ width: props.width }":placeholder"props.placeholder&quo…...

责任链模式案例

需求背景&#xff1a; 请你设计一个员工休假审批流程&#xff0c;当员工的休假天数<1时&#xff0c;由直接领导审批&#xff0c;休假天数<2时&#xff0c;分别由直接领导、一级部门领导审批&#xff0c;休假天数>3时&#xff0c;分别由直接领导、一级部门领导、分管领…...

Android NDK开发(二)——JNIEnv、jobject与jclass关系

本文主要讲解Android NDK开发中JNIEnv、jobject与jclass的相关知识&#xff0c;并用c和c两种语言实现了jobject和jclass。 本专栏知识点是通过<零声教育>的音视频流媒体高级开发课程进行系统学习&#xff0c;梳理总结后写下文章&#xff0c;对音视频相关内容感兴趣的读者…...

机器学习入门:sklearn基础教程

Scikit-learn&#xff08;简称sklearn&#xff09;是Python中最受欢迎的机器学习库之一&#xff0c;它提供了丰富的机器学习算法和工具&#xff0c;适用于各种任务和场景。本文将为您介绍sklearn的基础知识和常用功能&#xff0c;带您踏入机器学习的世界。 1. 安装与导入 首先…...

26 | 备库为什么会延迟好几个小时?

在官方的 5.6 版本之前,MySQL 只支持单线程复制,由此在主库并发高、TPS 高时就会出现严重的主备延迟问题。 coordinator 就是原来的 sql_thread, 不过现在它不再直接更新数据了,只负责读取中转日志和分发事务。真正更新日志的,变成了 worker 线程。而 work 线程的个数,就是…...

linux 如何解压.tar 文件

要在 Linux 中解压 tar 文件&#xff0c;请使用以下命令&#xff1a; tar -xvf yourfile.tar 1 其中&#xff0c;“yourfile.tar”是您要解压的文件名。 这个命令会将文件解压到当前目录中。如果想要将文件解压到不同的目录中&#xff0c;可以使用 -C 选项指定路径。例如&…...

盘点企业信息防泄密软件对比|揭秘企业信息防泄密软件好用榜

在当今信息化社会&#xff0c;企业信息防泄密软件的需求日益凸显。这些软件不仅关乎企业的核心竞争力&#xff0c;更直接关系到企业的生死存亡。本文将对市面上几款主流的企业信息防泄密软件进行深入对比分析&#xff0c;以期为企业提供有益的参考。 一、企业信息防泄密软件好…...

html--瀑布效果

<!doctype html> <html> <head> <meta charset"utf-8"> <title>瀑布效果</title><style> body {background: #222;color: white;overflow:hidden; }#container {box-shadow: inset 0 1px 0 #444, 0 -1px 0 #000;height: 1…...

vue视图不刷新强制更新数据this.$forceUpdate()

在vue中&#xff0c;更新视图数据&#xff0c;不刷新页面&#xff0c;需要强制更新数据才可以 前言 在对数据就行添加和删除时&#xff0c;发现页面视图不更新&#xff0c;排除发现需要强制更新才可以 点击添加或删除&#xff0c;新增数据和删除就行&#xff0c;但在不使用fo…...

2024年电工杯数学建模竞赛A题B题思路代码分享

您的点赞收藏是我继续更新的最大动力&#xff01; 一定要点击如下的卡片链接&#xff0c;那是获取资料的入口&#xff01; 点击链接加入群聊【2024电工杯】&#xff1a;http://qm.qq.com/cgi-bin/qm/qr?_wv1027&kUMFX8lu4qAm0XkZQ6JkW5m5O9F_mxf-L&authKey0hWdf7%2F…...

leetcode 797.所有可能的路径

思路&#xff1a;dfs。 其实很简单&#xff0c;我们只需要和昨天做的题一样&#xff0c;直接遍历所给数组中的元素&#xff0c;因为这里的数组意义已经很清楚了&#xff0c;就是当前位置的结点和哪一个顶点有联系。 注意&#xff1a;在存储路径的时候&#xff0c;我们需要按顺…...

自己怎么做卡密网站/百度快照的作用是什么

云计算在图书馆领域的应用与研究已陆续展开&#xff0c;图书馆的云时代即将到来。重塑图书馆生存和发展的环境、推动图书馆自身变革是云计算环境下图书馆发展的未来趋势。 云计算是分布式处理&#xff08;Distributed Computing&#xff09;、并行处理&#xff08;Parallel Com…...

需要做网站的企业资源/网络营销郑州优化推广公司

Git是一款开源的分布式版本控制系统&#xff0c;它的出现和Linux紧密相关。Linux内核项目组为了能更好地管理和维护Linux内核开发&#xff0c;于2002年开始启用商业的分布式版本控制系统BitKeeper。虽然软件开发商授权了Linux社区能免费使用&#xff0c;但是好景不长&#xff0…...

视频添加到wordpress/东莞推广平台有哪些

2019独角兽企业重金招聘Python工程师标准>>> 一般启动Tomcat时&#xff0c;都会用到的命令 nohup ./start.sh & nohup 命令 用途&#xff1a;退出终端后不挂断运行的命令&#xff0c;原程序的的标准输出被自动改向到当前目录下的nohup.out文件 & 的意思是直…...

太原商城网站建设/网络营销策划书的结构是什么

随时随地阅读更多技术实战干货&#xff0c;获取项目源码、学习资料&#xff0c;请关注源代码社区公众号(ydmsq666) 我们都知道Node.js现在得到了所有的关注。每个人都对学习Node.js感兴趣&#xff0c;并希望可以工作于Node.js。在开始工作之前了解技术背后的概念总是不会错的。…...

本地升级wordpress/关键词你们懂的

HTTP 的 Keep-Alive&#xff0c;是由应用层&#xff08;用户态&#xff09; 实现的&#xff0c;称为 HTTP 长连接&#xff1b; TCP 的 Keepalive&#xff0c;是由 TCP 层&#xff08;内核态&#xff09; 实现的&#xff0c;称为 TCP 保活机制 HTTP 的 Keep-Alive HTTP 是基于…...

网站开发属于技术合同/百度官网电话

一、Java反射机制概述 Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这…...