登录的几种方式
使用Session完成登录
1. 手机号发送验证码
逻辑步骤:
- 校验手机号格式是否正确。
- 生成验证码(例如使用Hutool工具类)。
- 将手机号和验证码存入Session。
- 返回验证码发送成功的响应。
2. 用户登录逻辑
逻辑步骤:
- 从Session中获取存储的手机号和验证码。
- 校验前端传来的手机号和验证码是否与Session中一致。
- 如果一致,根据手机号查询用户是否存在。
- 如果不存在,创建新用户并随机生成用户名。
- 将用户信息存入Session。
package com.hmdp.service.impl;import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.dto.LoginFormDTO;
import com.hmdp.dto.Result;
import com.hmdp.entity.User;
import com.hmdp.mapper.UserMapper;
import com.hmdp.service.IUserService;
import com.hmdp.utils.RegexUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;import javax.servlet.http.HttpSession;import static com.hmdp.utils.SystemConstants.USER_NICK_NAME_PREFIX;/*** <p>* 服务实现类* </p>** @author 虎哥* @since 2021-12-22*/
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {@Overridepublic Result sendCode(String phone, HttpSession session) {// 校验手机号if (RegexUtils.isPhoneInvalid(phone)) {// 不符合 返回错误信息return Result.fail("手机号格式错误");}// 生成验证码String code = RandomUtil.randomNumbers(6);// 保存验证码到Sessionsession.setAttribute("code",code);session.setAttribute("phone",phone);//TODO 发送验证码 需要调用第三方log.debug("发送验证码成功,验证码{}",code);// 返回成功return Result.ok();}@Overridepublic Result login(LoginFormDTO loginForm, HttpSession session) {// 校验手机号和验证码if (!loginForm.getPhone().equals(session.getAttribute("phone"))) {return Result.fail("手机号和之前的不同");}// 校验码不一致,报错if (!session.getAttribute("code").equals(loginForm.getCode())) {return Result.fail("验证码不正确");}// 一致,根据手机号查询用户User user = query().eq("phone", loginForm.getPhone()).one();if (user==null) {// 用户不存在 创建新的用户 保存用户到数据库,保存用户到Sessionuser = createUserWithPhone(loginForm.getPhone());}// 用户存在,直接保存用户到Sessionsession.setAttribute("user",user);return Result.ok();}private User createUserWithPhone(String phone) {User user = new User();user.setPhone(phone);user.setNickName(USER_NICK_NAME_PREFIX+RandomUtil.randomString(10)); // 设置默认的用户名save(user);return user;}
}
3. 登录校验拦截器
逻辑步骤:
- 拦截所有需要登录验证的请求。
- 检查Session中是否存在
user对象。 - 如果不存在,返回未登录的错误信息;否则将user信息转换为只含有id,昵称和头像地址的类之后(保护用户隐私信息)存入TreadLocal当中,并且放行。
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {Object user = request.getSession().getAttribute("user");if (user == null) {response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);response.getWriter().write("未登录,请先登录");return false;}// 将用户信息存入 ThreadLocal // 为了保护用户的隐私需要专门设置一个类只存储用户的昵id,称和头像地址UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);UserContext.setUser(userDTO );return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {// 清理 ThreadLocal 防止内存泄漏UserContext.clear();}
}
UserContext 工具类:
public class UserContext {private static final ThreadLocal<UserDTO> tl = new ThreadLocal<>();public static void saveUser(UserDTO user){tl.set(user);}public static UserDTO getUser(){return tl.get();}public static void removeUser(){tl.remove();}
}
还需要在MvcConfig当中配置拦截器
package com.hmdp.config;import com.hmdp.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class MvcConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 登录拦截器registry.addInterceptor(new LoginInterceptor()).excludePathPatterns("/user/code","/user/login","/blog/hot","/shop/**","/shop-type/**","/voucher/**").order(0);}
}
session 在服务器端有默认的时长(过期时间),这是由服务器配置决定的。默认情况下,Session 的有效期会受到服务器设置的影响,而无需手动设置时长。如果需要自定义时长,可以进行配置。 (默认值:
在大多数 Servlet 容器(如 Tomcat、Jetty)中,Session 的默认超时时间是 30分钟。
这个时间表示,如果用户在 30 分钟内没有访问服务器,Session 会被销毁。)
基于Redis代替Session登录
问题:Session 是存储在服务器内存中的,默认情况下每个服务器实例维护自己的 Session 数据。在分布式系统中,不同的请求可能被分配到不同的服务器实例,从而导致无法访问原始 Session。
虽然 Session 使用方便,但在分布式、高并发、跨平台场景下,其缺点可能带来较大的限制。因此,很多现代应用倾向于采用 无状态认证(如 JWT) 或集中式存储方案(如 Redis)来代替传统的 Session。选择方案时需要根据业务需求、系统架构和可接受的复杂性权衡决定。

1. 手机号发送验证码
逻辑:
- 校验手机号是否规范。
- 使用
Hutool工具生成验证码。 - 将验证码存入 Redis,设置过期时间为 2 分钟。
2. 用户登录逻辑
逻辑:
- 从 Redis 获取验证码,并与前端提交的验证码比对。
- 根据手机号查询用户,不存在则创建新用户。
- 生成登录令牌(
token),将用户信息转换为Hash并存入 Redis,设置有效期为 30 分钟。 - 返回
token给前端。
package com.hmdp.service.impl;import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.dto.LoginFormDTO;
import com.hmdp.dto.Result;
import com.hmdp.dto.UserDTO;
import com.hmdp.entity.User;
import com.hmdp.mapper.UserMapper;
import com.hmdp.service.IUserService;
import com.hmdp.utils.RegexUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import javax.servlet.http.HttpSession;import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;import static com.hmdp.utils.RedisConstants.*;
import static com.hmdp.utils.SystemConstants.USER_NICK_NAME_PREFIX;/*** <p>* 服务实现类* </p>** @author 虎哥* @since 2021-12-22*/
@Service
@Slf4j
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {final private StringRedisTemplate stringRedisTemplate;@Overridepublic Result sendCode(String phone, HttpSession session) {// 校验手机号if (RegexUtils.isPhoneInvalid(phone)) {// 不符合 返回错误信息return Result.fail("手机号格式错误");}// 生成验证码String code = RandomUtil.randomNumbers(6);/*// 保存验证码到Sessionsession.setAttribute("code",code);session.setAttribute("phone",phone);*/// 存储到Redis中stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY+phone,code,LOGIN_CODE_TTL, TimeUnit.MINUTES);//TODO 发送验证码 需要调用第三方log.debug("发送验证码成功,验证码{}",code);// 返回成功return Result.ok();}@Overridepublic Result login(LoginFormDTO loginForm, HttpSession session) {/*// 校验手机号和验证码if (!loginForm.getPhone().equals(session.getAttribute("phone"))) {return Result.fail("手机号和之前的不同");}// 校验码不一致,报错if (!session.getAttribute("code").equals(loginForm.getCode())) {return Result.fail("验证码不正确");}*///String code = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + loginForm.getPhone());if(code == null || ! code.equals(loginForm.getCode())){return Result.fail("验证码不正确或者手机号错误");}// 一致,根据手机号查询用户User user = query().eq("phone", loginForm.getPhone()).one();if (user==null) {// 用户不存在 创建新的用户 保存用户到数据库,保存用户到Sessionuser = createUserWithPhone(loginForm.getPhone());}// 用户存在,直接保存用户到Session
// session.setAttribute("user",user);// 保存到Redis中 以hash模式存储// 随机生成一个token作为登录令牌String token = UUID.randomUUID().toString(true);// 将User对象转为UserDTO 再以token为key,Hash形式存储UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);// 将userDTO转换为mapMap<String, Object> Usermap = BeanUtil.beanToMap(userDTO,new HashMap<>(),CopyOptions.create().setIgnoreNullValue(true).setFieldValueEditor((fieldName,fieldValue)-> fieldValue.toString()));stringRedisTemplate.opsForHash().putAll(LOGIN_USER_KEY +token,Usermap);// 设置有效期stringRedisTemplate.expire(LOGIN_USER_KEY+token,LOGIN_USER_TTL,TimeUnit.MINUTES);// 返回tokenreturn Result.ok(token);}private User createUserWithPhone(String phone) {User user = new User();user.setPhone(phone);user.setNickName(USER_NICK_NAME_PREFIX+RandomUtil.randomString(10)); // 设置默认的用户名save(user);return user;}
}
3. 登录拦截器
逻辑:
- 从请求头中获取
token。 - 使用
token从 Redis 获取用户信息。 - 如果用户信息为空,拦截请求。
- 将用户信息保存到
ThreadLocal。 - 刷新
token的有效期。
但是如果我们还是只在登录的拦截器当中刷新token的有效值,那么就只会在局部范围内保证token有效。而不是全局范围内,保证用户的token不会过期。
所以我们需要加一层 加一层拦截器(RefreshTokenInterceptor),虽然说是拦截器,但是他不进行拦截操作,拦截操作还是有LoginInterceptor进行拦截。
什么只在 LoginInterceptor 中刷新 token 不够?
局限性
LoginInterceptor 只对需要登录的接口进行拦截。如果用户只访问公开页面或非登录接口(如 /home、/shop 等),这些请求不会经过 LoginInterceptor,导致 token 无法刷新。
如果用户长时间浏览公开页面后访问需要登录的页面,可能因 token 过期被迫重新登录,影响用户体验。
全局活跃性保证
用户访问任何页面都应该被视为活跃状态,无论页面是否需要登录,都需要刷新 token 的有效期。单独依赖 LoginInterceptor 只能保证在局部范围(需要登录的接口)内刷新 token。
为什么需要 RefreshTokenInterceptor?
- RefreshTokenInterceptor 的目的是在全局范围内检测
token并刷新其有效期。 - 它负责在所有请求(无论是否需要登录)中检查
token,并且将用户保存到TreadLocal当中,但不进行登录状态的校验。 - 真正的拦截操作(判断用户是否登录)仍由
LoginInterceptor执行。(LoginInterceptor可以查看ThreadLocal中是否存在user就可以判断是否登录了)
RefreshTokenInterceptor的代码:
package com.hmdp.Interceptor;import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.hmdp.dto.UserDTO;
import com.hmdp.utils.UserHolder;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.concurrent.TimeUnit;import static com.hmdp.utils.RedisConstants.LOGIN_USER_KEY;
import static com.hmdp.utils.RedisConstants.LOGIN_USER_TTL;@RequiredArgsConstructor
public class RefreshTokenInterceptor implements HandlerInterceptor {final StringRedisTemplate stringRedisTemplate;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 从请求头当中获取tokenString token = request.getHeader("authorization");//如果 string token = "" ; 这个 token != null,而是 长度为0。所以不可以直接用 == nullif (StrUtil.isBlankIfStr(token)) {response.setStatus(401);return true;}String key = LOGIN_USER_KEY + token;// 从Redis获取用户Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(key);// 判断userMapif (userMap.isEmpty()) {response.setStatus(401);return true;}// 将userMap转换为BeanUserDTO userDTO= BeanUtil.fillBeanWithMap(userMap, new UserDTO(),false);
// UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);// 保存到ThreadLocal里面UserHolder.saveUser(userDTO);// 刷新token的有效期stringRedisTemplate.expire(key,LOGIN_USER_TTL, TimeUnit.MINUTES);return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {UserHolder.removeUser();}
}
LoginInterceptor拦截器代码:
package com.hmdp.Interceptor;
import com.hmdp.dto.UserDTO;
import com.hmdp.utils.UserHolder;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;// 判断是否需要拦截也就是TreadLocal当中是否存在user
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {UserDTO userDTO = UserHolder.getUser();if (userDTO == null) {response.setStatus(401);return false;}// 有用户放行return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {UserHolder.removeUser();}
}
MvcConfig配置
package com.hmdp.config;import com.hmdp.Interceptor.LoginInterceptor;
import com.hmdp.Interceptor.RefreshTokenInterceptor;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@RequiredArgsConstructor
public class MvcConfig implements WebMvcConfigurer {final StringRedisTemplate stringRedisTemplate;@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 拦截所有 执行顺序默认都是0,按照添加顺序执行,指定Order,越小越先执行// token刷新拦截器registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate)).order(0);// 拦截部分请求registry.addInterceptor(new LoginInterceptor()).excludePathPatterns("/user/code","/user/login","blog/hot","/shop/**","/shop-type/**","/voucher/**").order(1);}
}
MvcConfig作为Spring管理的Bean,可以通过构造注入或字段注入获取StringRedisTemplate。由于拦截器实例是手动创建的,MvcConfig需要将StringRedisTemplate显式传递给LoginInterceptor的构造方法。
相关文章:
登录的几种方式
使用Session完成登录 1. 手机号发送验证码 逻辑步骤: 校验手机号格式是否正确。生成验证码(例如使用Hutool工具类)。将手机号和验证码存入Session。返回验证码发送成功的响应。 2. 用户登录逻辑 逻辑步骤: 从Session中获取存…...
Scala_【5】函数式编程
第五章 函数式编程函数和方法的区别函数声明函数参数可变参数参数默认值 函数至简原则匿名函数高阶函数函数作为值传递函数作为参数传递函数作为返回值 函数闭包&柯里化函数递归控制抽象惰性函数友情链接 函数式编程 面向对象编程 解决问题时,分解对象ÿ…...
解析 World Football Cup 问题及其 Python 实现
问题描述 本文讨论一道关于足球锦标赛排名规则的问题,来自 Berland 足球协会对世界足球规则的调整。题目要求对给定的比赛数据进行计算,并输出能进入淘汰赛阶段的球队列表。以下是规则详细描述。 题目规则 输入格式: 第一行包含一个整数 …...
9.系统学习-卷积神经网络
9.系统学习-卷积神经网络 简介输入层卷积层感受野池化层全连接层代码实现 简介 卷积神经网络是一种用来处理局部和整体相关性的计算网络结构,被应用在图像识别、自然语言处理甚至是语音识别领域,因为图像数据具有显著的局部与整体关系,其在图…...
基于FPGA的出租车里程时间计费器
基于FPGA的出租车里程时间计费器 功能描述一、系统框图二、verilog代码里程增加模块时间增加模块计算价格模块上板视频演示 总结 功能描述 (1);里程计费功能:3公里以内起步价8元,超过3公里后每公里2元,其中…...
三甲医院等级评审八维数据分析应用(五)--数据集成与共享篇
一、引言 1.1 研究背景与意义 随着医疗卫生体制改革的不断深化以及信息技术的飞速发展,三甲医院评审作为衡量医院综合实力与服务水平的重要标准,对数据集成与共享提出了更为严苛的要求。在传统医疗模式下,医院内部各业务系统往往各自为政,形成诸多“信息孤岛”,使得数据…...
VUE条件树查询 自定义条件节点
之前实现过的简单的条件树功能如下图: 经过最新客户需求确认,上述条件树还需要再次改造,以满足正常需要! 最新暴改后的功能如下红框所示: 页面功能 主页面逻辑代码: <template><div class"…...
什么是打流,怎么用iperf3打流
什么是打流 在网络安全和黑灰产领域,“打流”具有不同的含义,常用于形容通过技术手段制造流量假象或发起流量攻击。 流量攻击(DDoS)中的“打流”: “打流”指向目标服务器或网络发起 大规模的数据请求,造…...
使用MySQL APT源在Linux上安装MySQL
全新安装MySQL的步骤 以下说明假定您的系统上尚未安装任何版本的MySQL(无论是由Oracle还是其他方分发) 添加MySQL的Apt源。 将MySQL的APT存储库添加到系统的软件存储库列表中。 1、转到MySQL APT存储库的下载页面MySQL :: Download MySQL APT Reposi…...
redux react-redux @reduxjs/toolkit
redux团队先后推出了redux、react-redux、reduxjs/toolkit,这三个库的api各有不同。本篇文章就来梳理一下当我们需要在项目中集成redux,从直接使用redux,到使用react-redux,再到react-redux和reduxjs/toolkit配合使用,…...
【偏好对齐】通过ORM直接推导出PRM
论文地址:https://arxiv.org/pdf/2412.01981 相关博客 【自然语言处理】【大模型】 ΨPO:一个理解人类偏好学习的统一理论框架 【强化学习】PPO:近端策略优化算法 【偏好对齐】PRM应该奖励单个步骤的正确性吗? 【偏好对齐】通过OR…...
Python与其他编程语言的区别是什么?
Python是一种广泛使用的高级编程语言,以其简洁的语法、强大的库支持和广泛的应用领域而著称。与其他编程语言相比,Python具有许多独特的特点和优势。以下将从多个方面详细探讨Python与其他编程语言的区别,并通过示例进行说明。 一、语法简洁…...
cuda11.6和对应的cudnn(windows)
因为每次不同的torch版本要下对应的cuda,这次刚好在Windows上下好了一个cuda11.6和对应的cudnn,直接放到网盘中,大家有需要对应版本的可以直接下载: 链接:https://pan.quark.cn/s/f153a53830d4 大家自取,c…...
24年无人机行业资讯 | 12.23-12.29
24年无人机行业资讯 | 12.23-12.29 1、 国家发改委新设低空经济司,助力低空经济规范发展2、商务部支持无人机民用国际贸易,强调出口管制与安全并重3、滨州高新区首架无人机成功下线4、 2025第九届世界无人机大会筹备推进会顺利召开5、2024年世界无人机竞…...
uniapp:微信小程序文本长按无法出现复制菜单
一、问题描述 在集成腾讯TUI后,为了能让聊天文本可以复制,对消息组件的样式进行修改,主要是移除下面的user-select属性限制: user-select: none;-webkit-user-select: none;-khtml-user-select: none;-moz-user-select: none;-ms…...
qml Item详解
1、概述 Item是QML(Qt Modeling Language)的基础元素,所有其他可视化元素都继承自它。它代表了一个可视化的对象,虽然Item对象本身没有可视外观,但它定义了所有可视项之间通用的属性,比如位置、大小、旋转…...
【Java回顾】Day4 反射机制
反射机制 之前学过一部分,笔记在20250103Java包_网络编程.md里,这里在之前的笔记的基础上做一些补充。 反射:得到class对象后反向获取对象的各种信息。 包 Field 类或接口中的字段(成员变量),动态访问和修改类的字段 模板 获取Class 对象 …...
【沉默的羔羊心理学】汉尼拔的“移情”游戏:操纵与理解的艺术,精神分析学视角下的角色互动
终极解读《沉默的羔羊》:弗洛伊德精神分析学视角下的深层剖析 关键词 沉默的羔羊弗洛伊德精神分析学角色心理意识与潜意识性别与身份 弗洛伊德精神分析学简介 弗洛伊德的精神分析学是心理学的一个重要分支,主要关注人类行为背后的无意识动机和冲突。…...
[深度学习] 大模型学习1-大语言模型基础知识
大语言模型(Large Language Model,LLM)是一类基于Transformer架构的深度学习模型,主要用于处理与自然语言相关的各种任务。简单来说,当用户输入文本时,模型会生成相应的回复或结果。它能够完成许多任务&…...
如何解决数据库和缓存不一致的问题
目录 一、Cache-Aside模式(旁路缓存模式) 二、Write-Through模式(写透缓存模式) 三、Write-Behind模式(写回缓存模式) 四、先删除缓存再更新数据库(不推荐,存在风险)…...
(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...
谷歌浏览器插件
项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...
【第二十一章 SDIO接口(SDIO)】
第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序
一、开发环境准备 工具安装: 下载安装DevEco Studio 4.0(支持HarmonyOS 5)配置HarmonyOS SDK 5.0确保Node.js版本≥14 项目初始化: ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
