从零开始的抽奖系统创作(2)

我们接着进行抽奖系统的完善。

前面我们完成了

1.结构初始化(统一结果返回之类的,还有包的分类)

2.加密(基于Hutool进行的对称与非对称加密)

3.用户注册

接下来我们先完善一下结构(统一异常处理)

1.统一异常处理

很简单,@RestControllerAdvice+@ExceptionHandler即可

@RestControllerAdvice//可以捕获全局抛出的异常
@ResponseBody
public class GlobalExceptionHandler {private final static Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);//捕获Service层的异常@ExceptionHandler(value = ServiceException.class)public CommonResult<?> serviceException(ServiceException e) {logger.error("serviceException :", e);//返回数据return CommonResult.error(GlobalErrorCodeConstant.INTERNAL_SERVICE_ERROR);}//捕获Controller层的异常@ExceptionHandler(value = ControllerException.class)public CommonResult<?> controllerException(ControllerException e) {logger.error("controllerException :", e);//返回数据return CommonResult.error(GlobalErrorCodeConstant.INTERNAL_SERVICE_ERROR);}//捕获全局的异常@ExceptionHandler(value = Exception.class)public CommonResult<?> exception(Exception e) {logger.error("exception :", e);//返回数据return CommonResult.error(GlobalErrorCodeConstant.INTERNAL_SERVICE_ERROR);}
}

这里使用@ExceptionHandler(value = 类名)的方式捕获异常。

其他没什么要特别注意的,补充GlobalErrorCodeConstant的异常种类

2.登录模块

这里提供短信验证码登录的方式,因此我们要了解一下阿里短信服务

但是现在个人申请不到了,所以我们直接使用虚拟的验证码吧。

附一个申请成功后植入的代码:

1.阿里短信代码模块:

依赖:

<dependency><groupId>com.aliyun</groupId><artifactId>dysmsapi20170525</artifactId><version>2.0.24</version>
</dependency>

短信服务工具类:

@Component
public class SMSUtil {private static final Logger logger = LoggerFactory.getLogger(SMSUtil.class);@Value(value = "${sms.sign-name}")private String signName;@Value(value = "${sms.access-key-id}")private String accessKeyId;@Value(value = "${sms.access-key-secret}")private String accessKeySecret;/*** 发送短信** @param templateCode 模板号* @param phoneNumbers 手机号* @param templateParam 模板参数 {"key":"value"}*/public void sendMessage(String templateCode, String phoneNumbers, String templateParam) {try {Client client = createClient();com.aliyun.dysmsapi20170525.models.SendSmsRequest sendSmsRequest = new SendSmsRequest().setSignName(signName).setTemplateCode(templateCode).setPhoneNumbers(phoneNumbers).setTemplateParam(templateParam);RuntimeOptions runtime = new RuntimeOptions();SendSmsResponse response = client.sendSmsWithOptions(sendSmsRequest, runtime);if (null != response.getBody()&& null != response.getBody().getMessage()&& "OK".equals(response.getBody().getMessage())) {logger.info("向{}发送信息成功,templateCode={}", phoneNumbers, templateCode);return;}logger.error("向{}发送信息失败,templateCode={},失败原因:{}",phoneNumbers, templateCode, response.getBody().getMessage());} catch (TeaException error) {logger.error("向{}发送信息失败,templateCode={}", phoneNumbers, templateCode, error);} catch (Exception _error) {TeaException error = new TeaException(_error.getMessage(), _error);logger.error("向{}发送信息失败,templateCode={}", phoneNumbers, templateCode, error);}}/*** 使用AK&SK初始化账号Client* @return Client*/private Client createClient() throws Exception {// 工程代码泄露可能会导致 AccessKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考。// 建议使用更安全的 STS 方式,更多鉴权访问方式请参见:https://help.aliyun.com/document_detail/378657.html。Config config = new Config().setAccessKeyId(accessKeyId).setAccessKeySecret(accessKeySecret);// Endpoint 请参考 https://api.aliyun.com/product/Dysmsapiconfig.endpoint = "dysmsapi.aliyuncs.com";return new Client(config);}}

配置:

### 短信 ##
## 短信 ##
sms.access-key-id="填写⾃⼰申请的"
sms.access-key-secret="填写⾃⼰申请的"
sms.sign-name="填写⾃⼰申请的"

代码使用:

使用时传入3个参数:

templateCode(模板号):SMS_465324787

phoneNumbers(手机号):传入你申请的

templateParam(模版参数):发送验证码的格式设置为{"key":"value"}

使用时传入一个map并将其序列化

2.验证码模块

利用Hutool工具:(这工具真好用都不用手写了)

//生成随机验证码
public class CaptchaUtil {/*** 生成随机验证码** @param length  几位* @return*/public static String getCaptcha(int length) {// 自定义纯数字的验证码(随机4位数字,可重复)RandomGenerator randomGenerator = new RandomGenerator("0123456789", length);LineCaptcha lineCaptcha = cn.hutool.captcha.CaptchaUtil.createLineCaptcha(200, 100);lineCaptcha.setGenerator(randomGenerator);// 重新生成codelineCaptcha.createCode();return lineCaptcha.getCode();}}

3.Controller

基于手机号生成验证码并发送验证码   最后使用Redis缓存验证码用于校验

    /*** 发送验证码* @param phoneNumber* @return*/@RequestMapping("/verification-code/send")public CommonResult<Boolean> verificationCode (String phoneNumber) {//日志打印logger.info("verificationCode  phoneNumber:{}", phoneNumber);verificationCodeService.sendVerificationCode(phoneNumber);return CommonResult.success(Boolean.TRUE);}

4.Service

    /*** 发送验证码* @param phoneNumber* @return*/public String sendVerificationCode(String phoneNumber);/*** 获取验证码* @param phoneNumber* @return*/public String getVerificationCode(String phoneNumber);
    @Overridepublic String sendVerificationCode(String phoneNumber) {//校验手机号if(!RegexUtil.checkMobile(phoneNumber)) {throw new ServiceException(ServiceErrorCodeConstant.PHONE_NUMBER_ERROR);}//生成随机验证码  利用hutool生成String code = CaptchaUtil.getCaptcha(4);//发送验证码Map<String, String> map = new HashMap<>();map.put("code", code);smsUtil.sendMessage(PHONE_NUMBER_TEMPLATE_CODE, phoneNumber, JacksonUtil.writeValueAsString(map));//缓存验证码redisUtil.set(PHONE_NUMBER_PRE +phoneNumber, code, PHONE_NUMBER_TIMEOUT);return redisUtil.get(PHONE_NUMBER_PRE +phoneNumber);}@Overridepublic String getVerificationCode(String phoneNumber) {//校验手机号if(!RegexUtil.checkMobile(phoneNumber)) {throw new ServiceException(ServiceErrorCodeConstant.PHONE_NUMBER_ERROR);}return redisUtil.get(PHONE_NUMBER_PRE +phoneNumber);}

Redis使用简单介绍:

配置:

在Linux服务器上通过隧道开放Redis的6379端口号

在Linux上输入命令启动Redis:

service redis-server start

 可以在idea上下载插件Redis Helper

刷新后在右侧找到插件点击加号添加Redis服务 

1.名称(随便,便于标识)

2.本机就行

3.在Linux隧道绑定的端口号

4.Test测试验证,出现绿色即成功连接Redis 

 Redis使用测试:

    @Testvoid redisTest() {stringRedisTemplate.opsForValue().set("key1", "value2");System.out.println("从redis中获取value : " + stringRedisTemplate.opsForValue().get("key1"));}@Testvoid redisUtil() {
//        redisUtil.set("key2", "value2");
//        redisUtil.set("key3", "value3", 20L);
//        System.out.println("key2是否存在: " + redisUtil.hasKey("key2"));
//        System.out.println("key3是否存在: " + redisUtil.hasKey("key3"));//        redisUtil.delete("key2");
//        System.out.println("key2是否存在: " + redisUtil.hasKey("key2"));System.out.println("key3是否存在: " + redisUtil.hasKey("key3"));}

1.使用前注入StringRedisTemplate类

2.使用stringRedisTemplate.opsForValue().set("key1", "value2");添加元素

但是,每次使用都要注入StringRedisTemplate有点麻烦了,我们将其封装成一个util工具。

@Configuration
public class RedisUtil {public static final Logger logger = LoggerFactory.getLogger(RedisUtil.class);/*** StringRedisTemplate : 直接用String存储读取(可读)* RedisTemplate : 先将被存储数据转换为字节数组(不可读)  再存储到Redis中 读取时以字节数组读取*/@Autowiredprivate StringRedisTemplate stringRedisTemplate;/*** 设置值* @param key* @param value* @return*/public boolean set(String key,String value) {try{stringRedisTemplate.opsForValue().set(key, value);return true;}catch(Exception e) {logger.error("RedisUtil set 错误:({}, {})", key, value, e);return false;}}/*** 设置带有过期时间的值* @param key* @param value* @param time  单位:秒* @return*/public boolean set(String key,String value, Long time) {try{stringRedisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);return true;}catch(Exception e) {logger.error("RedisUtil set 错误:({}, {}, {})", key, value, time, e);return false;}}/*** 获取值* @param key* @return*/public String get(String key) {try{return StringUtils.hasText(key)? stringRedisTemplate.opsForValue().get(key): null;}catch(Exception e) {logger.error("RedisUtil set 错误:({})", key, e);return null;}}/*** 删除值* @param key* @return*/public boolean delete(String... key) {try{if(key != null && key.length > 0) {if(key.length == 1) {stringRedisTemplate.delete(key[0]);}else {stringRedisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key));}}return true;}catch(Exception e) {logger.error("RedisUtil delete 错误:({})", key, e);return false;}}/*** 查看Key是否存在* @param key* @return*/public boolean hasKey(String key) {return StringUtils.hasText(key)? stringRedisTemplate.hasKey(key): false;}}

该类提供了:

set:两个,一个是传入key:value  。另一个是传入key:value+过期时间

get:通过key获取value

delete:通过key,进行批量删除

hasKey:查看key是否存在

5.JWT令牌验证

前面我们发送了验证码,在客户端我们就要将该验证码输入进行登录,但还有一些用户会采用手机号/邮箱+密码的方式登录。所以我们要开放两个接口用于登录。

登录完后端会返回给前端一个token用于令牌校验,使验证码登录在多台主机下都可以使用。

JWT本身没什么说的,直接上代码:

public class JWTUtil {private static final Logger logger = LoggerFactory.getLogger(JWTUtil.class);/*** 密钥:Base64编码的密钥*/private static final String SECRET = "SDKltwTl3SiWX62dQiSHblEB6O03FG9/vEaivFu6c6g=";/*** 生成安全密钥:将一个Base64编码的密钥解码并创建一个HMAC SHA密钥。*/private static final SecretKey SECRET_KEY = Keys.hmacShaKeyFor(Decoders.BASE64.decode(SECRET));/*** 过期时间(单位: 毫秒)*/private static final long EXPIRATION = 60*60*1000;/*** 生成密钥** @param claim  {"id": 12, "name":"张山"}* @return*/public static String genJwt(Map<String, Object> claim){//签名算法String jwt = Jwts.builder().setClaims(claim)             // 自定义内容(载荷).setIssuedAt(new Date())      // 设置签发时间.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION)) // 设置过期时间.signWith(SECRET_KEY)         // 签名算法.compact();return jwt;}/*** 验证密钥*/public static Claims parseJWT(String jwt){if (!StringUtils.hasLength(jwt)){return null;}// 创建解析器, 设置签名密钥JwtParserBuilder jwtParserBuilder = Jwts.parserBuilder().setSigningKey(SECRET_KEY);Claims claims = null;try {//解析tokenclaims = jwtParserBuilder.build().parseClaimsJws(jwt).getBody();}catch (Exception e){// 签名验证失败logger.error("解析令牌错误,jwt:{}", jwt, e);}return claims;}/*** 从token中获取用户ID*/public static Integer getUserIdFromToken(String jwtToken) {Claims claims = JWTUtil.parseJWT(jwtToken);if (claims != null) {Map<String, Object> userInfo = new HashMap<>(claims);return (Integer) userInfo.get("userId");}return null;}
}

提供了三个方法:

1.getJWT:通过传入一个map生成token并返回。

2.parseJWT:将token解析为map数据

3.getUserIdFromToken:从token中获取用户ID(可能有用)

6.管理员登录(两种⽅式)

学习令牌的使⽤之后, 接下来我们通过令牌来完成⽤⼾的登录的流程

1. 登陆⻚⾯把⽤⼾名密码提交给服务器.

2. 服务器端验证⽤⼾名密码是否正确, 如果正确, 服务器⽣成令牌, 下发给客⼾端.

3. 客⼾端把令牌存储起来(⽐如Cookie, local storage等), 后续请求时, 把token发给服务器

4. 服务器对令牌进⾏校验, 如果令牌正确, 进⾏下⼀步操作

7.Controller

数据准备:

接收与返回的数据:

因为两个登录都含有统一数据,所以对公共的进行提取

//共有的字段
@Data
public class UserLoginParam implements Serializable {/*** 身份登录信息* 可填可不填  不填代表都可以登录*/private String mandatoryIdentity;
}

UserPasswordLoginParam:

@Data
@EqualsAndHashCode(callSuper = true)
public class UserPasswordLoginParam extends UserLoginParam{/*** 手机号或邮箱*/@NotBlank(message = "手机号或邮箱不能为空")private String loginName;/*** 密码*/@NotBlank(message = "密码不能为空")private String password;
}

UserMessageLoginParam:

@Data
@EqualsAndHashCode(callSuper = true)
public class UserMessageLoginParam extends UserLoginParam{/*** 登录手机号*/@NotBlank(message = "手机号不能为空")private String loginMobile;/*** 验证码*/@NotBlank(message = "验证码不能为空")private String code;
}

UserLoginResult:

@Data
public class UserLoginResult implements Serializable {/*** 令牌*/@NotBlank(message = "令牌不能为空")private String token;/*** 身份信息*/@NotBlank(message = "身份信息不能为空")private String identity;
}

1.密码登录:

    /*** 密码登录* @param userLoginParam* @return*/@RequestMapping("/password/login")private CommonResult<UserLoginResult> passwordLogin(@Validated @RequestBody UserPasswordLoginParam userLoginParam) {logger.info("passwordLogin userLoginParam:{}", JacksonUtil.writeValueAsString(userLoginParam));//使用同一个接口完成登录UserLoginDTO userLoginDTO = userService.login(userLoginParam);return CommonResult.success(convertToLoginResult(userLoginDTO));}

在数据库返回的对象使用DTO,然后返回时用convertToLoginResult进行类型转化。

    private UserLoginResult convertToLoginResult(UserLoginDTO userLoginDTO) {//校验if(userLoginDTO == null) {throw new ControllerException(ControllerErrorCodeConstant.LOGIN_ERROR);}//数据转化UserLoginResult userLoginResult = new UserLoginResult();userLoginResult.setToken(userLoginDTO.getToken());userLoginResult.setIdentity(userLoginDTO.getIdentity().name());return userLoginResult;}

2.短信验证码登录

与之类似:

    /*** 短信验证码登录* @param userLoginParam* @return*/@RequestMapping("/message/login")private CommonResult<UserLoginResult> messageLogin(@Validated @RequestBody UserMessageLoginParam userLoginParam) {logger.info("messageLogin userLoginParam:{}", JacksonUtil.writeValueAsString(userLoginParam));//使用同一个接口完成登录  将接收参数改为公用参数extendsUserLoginDTO userLoginDTO = userService.login(userLoginParam);return CommonResult.success(convertToLoginResult(userLoginDTO));}

8.Sevice

通过Java14特性属性校验并赋值实现一个接口完成两个登录功能

    /*** 用户登录*   1.手机号/邮箱 + 密码*   2.手机号 + 验证码* @param userLoginParam* @return*/@Overridepublic UserLoginDTO login(UserLoginParam userLoginParam) {UserLoginDTO userLoginDTO = null;//类型检查与类型交换  java 14 版本及以上  实现校验两个登录方式if(userLoginParam instanceof UserPasswordLoginParam loginParam) {//手机号/邮箱 + 密码userLoginDTO = loginByPassword(loginParam);}else if(userLoginParam instanceof UserMessageLoginParam loginParam) {//手机号 + 验证码userLoginDTO = loginByShortMessage(loginParam);}else {throw new ServiceException(ServiceErrorCodeConstant.LOGIN_INFO_NOT_EXITS);}return userLoginDTO;}

DTO:

@Data
public class UserLoginDTO implements Serializable {/*** JWT令牌*/private String token;/*** 身份信息*/private UserIdentityEnum identity;
}

1.通过手机短信登录

1.校验手机号

        if(!StringUtils.hasText(loginParam.getLoginMobile())) {throw new ServiceException(ServiceErrorCodeConstant.PHONE_NUMBER_ERROR);}

2.通过手机号完成数据库查询

        UserDO userDO = userMapper.selectByPhoneNumber(new Encrypt(loginParam.getLoginMobile()));

3.校验数据库信息及身份(判断是否是管理员)

        //校验数据库数据if(userDO == null) {throw new ServiceException(ServiceErrorCodeConstant.USER_INFO_IS_EMPTY);}else if(StringUtils.hasText(loginParam.getMandatoryIdentity())&& !loginParam.getMandatoryIdentity().equalsIgnoreCase(userDO.getIdentity())) {//身份校验不通过throw new ServiceException(ServiceErrorCodeConstant.IDENTITY_ERROR);}

4.获取Redis中的验证码并于数据库中的数据进行校验 

        String code = verificationCodeService.getVerificationCode(loginParam.getLoginMobile());if(!code.equals(loginParam.getCode())) {throw new ServiceException(ServiceErrorCodeConstant.VERIFICATION_CODE_ERROR);}

5.将数据封装成一个token

        //塞入返回值(JWT)Map<String, Object> claim = new HashMap<>();claim.put("id", userDO.getId());claim.put("identity", userDO.getIdentity());String token = JWTUtil.getJwt(claim);

6.封装成为DTO返回

        UserLoginDTO userLoginDTO = new UserLoginDTO();userLoginDTO.setToken(token);userLoginDTO.setIdentity(UserIdentityEnum.forName(userDO.getIdentity()));return userLoginDTO;

2.通过手机号/邮箱+密码登录

1.校验密码

2.判断是手机号还是邮箱

3.通过手机号/邮箱获取数据库信息

4.校验 数据库信息及身份信息

5.生成token

6.包装成DTO返回

    /*** 通过手机号+密码登录* @param loginParam* @return*/private UserLoginDTO loginByPassword(UserPasswordLoginParam loginParam) {UserDO userDO = null;if(!StringUtils.hasText(loginParam.getPassword())) {throw new ServiceException(ServiceErrorCodeConstant.PASSWORD_EMPTY);}//判断是手机号还是邮箱登录if(RegexUtil.checkMobile(loginParam.getLoginName())) {//手机号//根据手机号查询用户表userDO = userMapper.selectByPhoneNumber(new Encrypt(loginParam.getLoginName()));}else if(RegexUtil.checkMail(loginParam.getLoginName())) {//邮箱登录//根据邮箱查询用户表userDO = userMapper.selectByEmail(loginParam.getLoginName());}else {throw new ServiceException(ServiceErrorCodeConstant.LOGIN_NOT_EXITS);}//校验登录信息if(userDO == null) {throw new ServiceException(ServiceErrorCodeConstant.USER_INFO_IS_EMPTY);}else if(StringUtils.hasText(loginParam.getMandatoryIdentity())&& !loginParam.getMandatoryIdentity().equalsIgnoreCase(userDO.getIdentity())) {//身份校验不通过throw new ServiceException(ServiceErrorCodeConstant.IDENTITY_ERROR);}else if(!DigestUtil.sha256Hex(loginParam.getPassword()).equals(userDO.getPassword())) {throw new ServiceException(ServiceErrorCodeConstant.PASSWORD_ERROR);}//塞入返回值(JWT)Map<String, Object> claim = new HashMap<>();claim.put("id", userDO.getId());claim.put("identity", userDO.getIdentity());String token = JWTUtil.getJwt(claim);UserLoginDTO userLoginDTO = new UserLoginDTO();userLoginDTO.setToken(token);userLoginDTO.setIdentity(UserIdentityEnum.forName(userDO.getIdentity()));return userLoginDTO;}

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

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

相关文章

【vs2022的C#窗体项目】打开运行+sql Server改为mysql数据库+发布

1. vs2022打开运行原sql Server的C#窗体项目更改为mysql数据库 1.1. vs2022安装基础模块即可 安装1️⃣vs核心编辑器2️⃣.net桌面开发必选&#xff0c;可选均不安装&#xff01;&#xff01;&#xff01; 为了成功连接mysql数据库&#xff0c;需要安装组件NuGet包管理器 安…

AI 编程 “幻觉” 风险频发?飞算 JavaAI 硬核技术筑牢安全防线

AI 技术已深度融入编程领域&#xff0c;为开发者带来前所未有的便利与效率提升。然而&#xff0c;AI 编程 “幻觉” 问题如影随形&#xff0c;频频引发困扰&#xff0c;成为阻碍行业稳健发展的潜在风险。飞算 JavaAI 凭借一系列硬核技术&#xff0c;强势出击&#xff0c;为攻克…

数据库----软考中级软件设计师(自用学习笔记)

目录 1、E-R图 2、结构数据模型 3、数据库的三级模式结构 4、关系代数 5、查询 6、SQL控制语句 7、视图​编辑 8、索引 9、关系模式 10、函数依赖 11、通过闭包求候选码 12、范式 13、无损连接和保持函数依赖 14、数据库设计 15、数据库的控制功能 16、数据库…

【Qt】Qt常见控件的相关知识点

1.close退出槽函数 2.设置快捷键&#xff0c;QMenu 。 适用&字母就能设置快捷键&#xff0c;运行qt程序&#xff0c;最后就可以按Alt对应的字母进行快捷操作。 3.QMenuBar内存泄露问题 如果ui已经自动生成了menubar&#xff0c;我们再次生成一个新的菜单栏&#xff0c;而…

httpx[http2] 和 httpx 的核心区别及使用场景如下

httpx[http2] 和 httpx 的核心区别在于 HTTP/2 协议支持&#xff0c;具体差异及使用场景如下&#xff1a; 1. 功能区别 命令/安装方式协议支持额外依赖适用场景pip install httpx仅 HTTP/1.1无通用请求&#xff0c;轻量依赖pip install httpx[http2]支持 HTTP/2需安装 h2>3…

Spring Boot 中 MyBatis 与 Spring Data JPA 的对比介绍

一、核心概念 MyBatis 定义&#xff1a;基于 SQL 的持久层框架&#xff0c;提供灵活的 SQL 映射和自定义查询能力。 特点&#xff1a; 开发者手动编写 SQL&#xff08;XML 或注解&#xff09;。 支持动态 SQL、复杂查询优化。 轻量级&#xff0c;对数据库控制力强。 Spri…

k8s1.27集群部署mysql8.0双主双从

环境介绍&#xff1a; #节点分配 159m--->两个master&#xff0c;生产环境建议&#xff0c;一个master一个节点。 160n-->slave-0 161n-->slaves-0 #存储卷 pv-->放在节点上&#xff0c;没用nfs/云存储。hostpath方式存储。pv的资源分配1G&#xff0c;较小&#…

vivado fpga程序固化

一般下载到fpga上的程序在掉电之后就会丢失&#xff0c;如果想要掉电之后程序不丢失&#xff0c;就需要将比特流文件固化到板载的flash上。 以下以我的7a100t开发板为例&#xff0c;介绍程序固化的流程 点击OK就可以下载了。

RabbitMQ Topic RPC

Topics(通配符模式) Topics 和Routing模式的区别是: topics 模式使⽤的交换机类型为topic(Routing模式使⽤的交换机类型为direct)topic 类型的交换机在匹配规则上进⾏了扩展, Binding Key⽀持通配符匹配(direct类型的交换机路 由规则是BindingKey和RoutingKey完全匹配) 在top…

服务器死机了需要检查哪些问题

在这个数字化的时代&#xff0c;服务器就像是我们信息世界的“大管家”&#xff0c;可要是它突然死机了&#xff0c;那可真是让人头疼。今天咱们就来聊聊&#xff0c;服务器死机了&#xff0c;到底需要检查哪些问题。 一、硬件问题 电源供应&#xff1a;检查电源是否稳定&…

【MySQL成神之路】运算符总结

MySQL运算符总结 MySQL提供了丰富的运算符&#xff0c;用于在SQL语句中进行各种计算和比较操作。这些运算符可以分为算术运算符、比较运算符、逻辑运算符、位运算符等几大类。合理使用这些运算符可以构建复杂的查询条件和计算表达式。 一、算术运算符 MySQL支持基本的算术运…

自用Vscode 配置c++ debug环境

前言 使用vscode配置c debug环境的好处 1、可以借助vscode方便轻量的扩展和功能 2、避免了传统使用gdb 复杂按键以及不够直观的可视化 3、方便一次运行&#xff0c;断点处查看变量&#xff0c;降低找bug难度 4、某大公司项目采用类似配置&#xff0c;经过实践检验 配置c运行环…

创建一个使用 GPT-4o 和 SERP 数据的 RAG 聊天机器人

亮数据-网络IP代理及全网数据一站式服务商屡获殊荣的代理网络、强大的数据挖掘工具和现成可用的数据集。亮数据&#xff1a;网络数据平台领航者https://www.bright.cn/?promogithub15?utm_sourceorganic-social-cn&utm_campaigncsdn 本指南将解释如何使用 Python、GPT-4…

吴恩达 Deep Learning(1-36)ppt逐行理解

课程地址&#xff1a;(超爽中英!) 2024公认最好的【吴恩达深度学习】教程&#xff01;附课件代码 Professionalization of Deep Learning_哔哩哔哩_bilibili 1.目录 2.什么是神经网络 3.用神经网络进行监督学习 4.为什么深度学习会兴起 7.二分分类 适用于二元分类问题的函数&…

三维点云的处理

1 点云原理 https://zh.wikipedia.org/wiki/%E9%BB%9E%E9%9B%B2 点云&#xff08;英语&#xff1a;point cloud&#xff09;是空间中点的数据集&#xff0c;可以表示三维形状或对象&#xff0c;通常由三维扫描仪获取。点云中每个点的位置都由一组笛卡尔坐标(X,Y,Z)描述[1]&…

鸿蒙HarmonyOS多设备流转:分布式的智能协同技术介绍

随着物联网和智能设备的普及&#xff0c;多设备间的无缝协作变得越来越重要。鸿蒙&#xff08;HarmonyOS&#xff09;作为华为推出的新一代操作系统&#xff0c;其分布式技术为实现多设备流转提供了强大的支持。本文将详细介绍鸿蒙多设备流转的技术原理、实现方式和应用场景。 …

Spring Boot- 2 (数万字入门教程 ):数据交互篇

JDBC交互框架: Spring的JDBC操作工具: 依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> JDBC的模版类:JdbcTemplate 引入Mysql的依赖 <depe…

在 Kotlin 中,什么是内联函数?有什么作用?

在 Kotlin 中&#xff0c;内联函数是一种通过 inline 关键字声明的函数&#xff0c;其主要目的是优化高阶函数&#xff08;即以函数作为参数或返回值的函数&#xff09;的性能。 内联函数的函数体会在编译时直接插入到调用处&#xff0c;从而避免函数调用的开销&#xff0c;并…

LLM笔记(五)概率论

1. 随机变量与概率分布&#xff1a;模型输出的基础 在LLM中&#xff0c;随机变量最直观的体现就是模型预测的下一个token。每个时刻&#xff0c;模型都会输出一个概率分布&#xff0c;表示词汇表中每个token可能是"下一个词"的概率。 直观理解 想象模型在处理句子…

LeetCode-滑动窗口-找到字符串中所有字母异位词

LeetCode-滑动窗口-找到字符串中所有字母异位词 ✏️ 关于专栏&#xff1a;专栏用于记录 prepare for the coding test。 文章目录 LeetCode-滑动窗口-找到字符串中所有字母异位词&#x1f4dd; 找到字符串中所有字母异位词&#x1f3af;题目描述&#x1f50d; 输入输出示例&am…