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

谷粒商城—分布式高级②.md

认证服务

1. 环境搭建

创建gulimall-auth-server模块,导依赖,引入login.htmlreg.html,并把静态资源放到nginx的static目录下

2. 注册功能

(1) 验证码倒计时

在这里插入图片描述

//点击发送验证码按钮触发下面函数
$("#sendCode").click(function () {//如果有disabled,说明最近已经点过,则什么都不做if($(this).hasClass("disabled")){}else {//调用函数使得当前的文本进行倒计时功能timeOutChangeStyle();//发送验证码var phone=$("#phoneNum").val();$.get("/sms/sendCode?phone="+phone,function (data){if (data.code!=0){alert(data.msg);}})}})let time = 60;function timeOutChangeStyle() {//开启倒计时后设置标志属性disable,使得该按钮不能再次被点击$("#sendCode").attr("class", "disabled");//当时间为0时,说明倒计时完成,则重置if(time==0){$("#sendCode").text("点击发送验证码");time=60;$("#sendCode").attr("class", "");}else {//每秒调用一次当前函数,使得time--$("#sendCode").text(time+"s后再次发送");time--;setTimeout("timeOutChangeStyle()", 1000);}}
(2) 整合短信服务

在阿里云网页购买试用的短信服务

gulimall-third-party中编写发送短信组件,其中hostpathappcode可以在配置文件中使用前缀spring.cloud.alicloud.sms进行配置

@Data
@ConfigurationProperties(prefix = "spring.cloud.alicloud.sms")
@Controller
public class SmsComponent {private String host;private String path;private String appcode;public void sendCode(String phone,String code) {
//        String host = "http://dingxin.market.alicloudapi.com";
//        String path = "/dx/sendSms";String method = "POST";
//        String appcode = "你自己的AppCode";Map<String, String> headers = new HashMap<String, String>();//最后在header中的格式(中间是英文空格)为Authorization:APPCODE 83359fd73fe94948385f570e3c139105headers.put("Authorization", "APPCODE " + appcode);Map<String, String> querys = new HashMap<String, String>();querys.put("mobile",phone);querys.put("param", "code:"+code);querys.put("tpl_id", "TP1711063");Map<String, String> bodys = new HashMap<String, String>();try {/*** 重要提示如下:* HttpUtils请从* https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/src/main/java/com/aliyun/api/gateway/demo/util/HttpUtils.java* 下载** 相应的依赖请参照* https://github.com/aliyun/api-gateway-demo-sign-java/blob/master/pom.xml*/HttpResponse response = HttpUtils.doPost(host, path, method, headers, querys, bodys);System.out.println(response.toString());//获取response的body//System.out.println(EntityUtils.toString(response.getEntity()));} catch (Exception e) {e.printStackTrace();}}
}

编写controller,给别的服务提供远程调用发送验证码的接口

@Controller
@RequestMapping(value = "/sms")
public class SmsSendController {@Resourceprivate SmsComponent smsComponent;/*** 提供给别的服务进行调用* @param phone 电话号码* @param code 验证码* @return*/@ResponseBody@GetMapping(value = "/sendCode")public R sendCode(@RequestParam("phone") String phone, @RequestParam("code") String code) {//发送验证码smsComponent.sendCode(phone,code);System.out.println(phone+code);return R.ok();}
}
(3) 接口防刷

由于发送验证码的接口暴露,为了防止恶意攻击,我们不能随意让接口被调用。

  • 在redis中以phone-code将电话号码和验证码进行存储并将当前时间与code一起存储
    • 如果调用时以当前phone取出的v不为空且当前时间在存储时间的60s以内,说明60s内该号码已经调用过,返回错误信息
    • 60s以后再次调用,需要删除之前存储的phone-code
    • code存在一个过期时间,我们设置为10min,10min内验证该验证码有效
@GetMapping("/sms/sendCode")
@ResponseBody
public R sendCode(@RequestParam("phone")String phone) {//接口防刷,在redis中缓存phone-codeValueOperations<String, String> ops = redisTemplate.opsForValue();String prePhone = AuthServerConstant.SMS_CODE_CACHE_PREFIX + phone;String v = ops.get(prePhone);if (!StringUtils.isEmpty(v)) {long pre = Long.parseLong(v.split("_")[1]);//如果存储的时间小于60s,说明60s内发送过验证码if (System.currentTimeMillis() - pre < 60000) {return R.error(BizCodeEnum.SMS_CODE_EXCEPTION.getCode(), BizCodeEnum.SMS_CODE_EXCEPTION.getMsg());}}//如果存在的话,删除之前的验证码redisTemplate.delete(prePhone);//获取到6位数字的验证码String code = String.valueOf((int)((Math.random() + 1) * 100000));//在redis中进行存储并设置过期时间ops.set(prePhone,code+"_"+System.currentTimeMillis(),10, TimeUnit.MINUTES);thirdPartFeignService.sendCode(phone, code);return R.ok();
}
(4) 注册接口编写

gulimall-auth-server服务中编写注册的主体逻辑

  • 若JSR303校验未通过,则通过BindingResult封装错误信息,并重定向至注册页面
  • 若通过JSR303校验,则需要从redis中取值判断验证码是否正确,正确的话通过会员服务注册
  • 会员服务调用成功则重定向至登录页,否则封装远程服务返回的错误信息返回至注册页面

注: RedirectAttributes可以通过session保存信息并在重定向的时候携带过去

 @PostMapping("/register")public String register(@Valid UserRegisterVo registerVo, BindingResult result, RedirectAttributes attributes) {//1.判断校验是否通过Map<String, String> errors = new HashMap<>();if (result.hasErrors()){//1.1 如果校验不通过,则封装校验结果result.getFieldErrors().forEach(item->{errors.put(item.getField(), item.getDefaultMessage());//1.2 将错误信息封装到session中attributes.addFlashAttribute("errors", errors);});//1.2 重定向到注册页return "redirect:http://auth.gulimall.com/reg.html";}else {//2.若JSR303校验通过//判断验证码是否正确String code = redisTemplate.opsForValue().get(AuthServerConstant.SMS_CODE_CACHE_PREFIX + registerVo.getPhone());//2.1 如果对应手机的验证码不为空且与提交上的相等-》验证码正确if (!StringUtils.isEmpty(code) && registerVo.getCode().equals(code.split("_")[0])) {//2.1.1 使得验证后的验证码失效redisTemplate.delete(AuthServerConstant.SMS_CODE_CACHE_PREFIX + registerVo.getPhone());//2.1.2 远程调用会员服务注册R r = memberFeignService.register(registerVo);if (r.getCode() == 0) {//调用成功,重定向登录页return "redirect:http://auth.gulimall.com/login.html";}else {//调用失败,返回注册页并显示错误信息String msg = (String) r.get("msg");errors.put("msg", msg);attributes.addFlashAttribute("errors", errors);return "redirect:http://auth.gulimall.com/reg.html";}}else {//2.2 验证码错误errors.put("code", "验证码错误");attributes.addFlashAttribute("errors", errors);return "redirect:http://auth.gulimall.com/reg.html";}}}

通过gulimall-member会员服务注册逻辑

  • 通过异常机制判断当前注册会员名和电话号码是否已经注册,如果已经注册,则抛出对应的自定义异常,并在返回时封装对应的错误信息
  • 如果没有注册,则封装传递过来的会员信息,并设置默认的会员等级、创建时间
 @RequestMapping("/register")public R register(@RequestBody MemberRegisterVo registerVo) {try {memberService.register(registerVo);//异常机制:通过捕获对应的自定义异常判断出现何种错误并封装错误信息} catch (UserExistException userException) {return R.error(BizCodeEnum.USER_EXIST_EXCEPTION.getCode(), BizCodeEnum.USER_EXIST_EXCEPTION.getMsg());} catch (PhoneNumExistException phoneException) {return R.error(BizCodeEnum.PHONE_EXIST_EXCEPTION.getCode(), BizCodeEnum.PHONE_EXIST_EXCEPTION.getMsg());}return R.ok();}
public void register(MemberRegisterVo registerVo) {//1 检查电话号是否唯一checkPhoneUnique(registerVo.getPhone());//2 检查用户名是否唯一checkUserNameUnique(registerVo.getUserName());//3 该用户信息唯一,进行插入MemberEntity entity = new MemberEntity();//3.1 保存基本信息entity.setUsername(registerVo.getUserName());entity.setMobile(registerVo.getPhone());entity.setCreateTime(new Date());//3.2 使用加密保存密码BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();String encodePassword = passwordEncoder.encode(registerVo.getPassword());entity.setPassword(encodePassword);//3.3 设置会员默认等级//3.3.1 找到会员默认登记MemberLevelEntity defaultLevel = memberLevelService.getOne(new QueryWrapper<MemberLevelEntity>().eq("default_status", 1));//3.3.2 设置会员等级为默认entity.setLevelId(defaultLevel.getId());// 4 保存用户信息this.save(entity);
}private void checkUserNameUnique(String userName) {Integer count = baseMapper.selectCount(new QueryWrapper<MemberEntity>().eq("username", userName));if (count > 0) {throw new UserExistException();}
}private void checkPhoneUnique(String phone) {Integer count = baseMapper.selectCount(new QueryWrapper<MemberEntity>().eq("mobile", phone));if (count > 0) {throw new PhoneNumExistException();}
}

3. 用户名密码登录

gulimall-auth-server模块中的主体逻辑

  • 通过会员服务远程调用登录接口
    • 如果调用成功,重定向至首页
    • 如果调用失败,则封装错误信息并携带错误信息重定向至登录页
@RequestMapping("/login")
public String login(UserLoginVo vo,RedirectAttributes attributes){R r = memberFeignService.login(vo);if (r.getCode() == 0) {return "redirect:http://gulimall.com/";}else {String msg = (String) r.get("msg");Map<String, String> errors = new HashMap<>();errors.put("msg", msg);attributes.addFlashAttribute("errors", errors);return "redirect:http://auth.gulimall.com/login.html";}
}

gulimall-member模块中完成登录

  • 当数据库中含有以当前登录名为用户名或电话号且密码匹配时,验证通过,返回查询到的实体
  • 否则返回null,并在controller返回用户名或密码错误
@RequestMapping("/login")
public R login(@RequestBody MemberLoginVo loginVo) {MemberEntity entity=memberService.login(loginVo);if (entity!=null){return R.ok();}else {return R.error(BizCodeEnum.LOGINACCT_PASSWORD_EXCEPTION.getCode(), BizCodeEnum.LOGINACCT_PASSWORD_EXCEPTION.getMsg());}
}@Overridepublic MemberEntity login(MemberLoginVo loginVo) {String loginAccount = loginVo.getLoginAccount();//以用户名或电话号登录的进行查询MemberEntity entity = this.getOne(new QueryWrapper<MemberEntity>().eq("username", loginAccount).or().eq("mobile", loginAccount));if (entity!=null){BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();boolean matches = bCryptPasswordEncoder.matches(loginVo.getPassword(), entity.getPassword());if (matches){entity.setPassword("");return entity;}}return null;}

4. 社交登录

(1) oauth2.0

在这里插入图片描述

(2) 在微博开放平台创建应用

在这里插入图片描述

(3) 在登录页引导用户至授权页
GET
https://api.weibo.com/oauth2/authorize?client_id=YOUR_CLIENT_ID&response_type=code&redirect_uri=YOUR_REGISTERED_REDIRECT_URI
  • client_id: 创建网站应用时的app key
  • YOUR_REGISTERED_REDIRECT_URI: 认证完成后的跳转链接(需要和平台高级设置一致)

在这里插入图片描述

如果用户同意授权,页面跳转至 YOUR_REGISTERED_REDIRECT_URI/?code=CODE

code是我们用来换取令牌的参数

(4) 换取token
POST
https://api.weibo.com/oauth2/access_token?client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&grant_type=authorization_code&redirect_uri=YOUR_REGISTERED_REDIRECT_URI&code=CODE
  • client_id: 创建网站应用时的app key
  • client_secret: 创建网站应用时的app secret
  • YOUR_REGISTERED_REDIRECT_URI: 认证完成后的跳转链接(需要和平台高级设置一致)
  • code:换取令牌的认证码

返回数据如下

在这里插入图片描述

(5) 获取用户信息

https://open.weibo.com/wiki/2/users/show

结果返回json

(6) 代码编写

认证接口

  • 通过HttpUtils发送请求获取token,并将token等信息交给member服务进行社交登录
  • 若获取token失败或远程调用服务失败,则封装错误信息重新转回登录页
@Controller
public class OauthController {@Autowiredprivate MemberFeignService memberFeignService;@RequestMapping("/oauth2.0/weibo/success")public String authorize(String code, RedirectAttributes attributes) throws Exception {//1. 使用code换取token,换取成功则继续2,否则重定向至登录页Map<String, String> query = new HashMap<>();query.put("client_id", "2144***074");query.put("client_secret", "ff63a0d8d5*****29a19492817316ab");query.put("grant_type", "authorization_code");query.put("redirect_uri", "http://auth.gulimall.com/oauth2.0/weibo/success");query.put("code", code);//发送post请求换取tokenHttpResponse response = HttpUtils.doPost("https://api.weibo.com", "/oauth2/access_token", "post", new HashMap<String, String>(), query, new HashMap<String, String>());Map<String, String> errors = new HashMap<>();if (response.getStatusLine().getStatusCode() == 200) {//2. 调用member远程接口进行oauth登录,登录成功则转发至首页并携带返回用户信息,否则转发至登录页String json = EntityUtils.toString(response.getEntity());SocialUser socialUser = JSON.parseObject(json, new TypeReference<SocialUser>() {});R login = memberFeignService.login(socialUser);//2.1 远程调用成功,返回首页并携带用户信息if (login.getCode() == 0) {String jsonString = JSON.toJSONString(login.get("memberEntity"));MemberResponseVo memberResponseVo = JSON.parseObject(jsonString, new TypeReference<MemberResponseVo>() {});attributes.addFlashAttribute("user", memberResponseVo);return "redirect:http://gulimall.com";}else {//2.2 否则返回登录页errors.put("msg", "登录失败,请重试");attributes.addFlashAttribute("errors", errors);return "redirect:http://auth.gulimall.com/login.html";}}else {errors.put("msg", "获得第三方授权失败,请重试");attributes.addFlashAttribute("errors", errors);return "redirect:http://auth.gulimall.com/login.html";}}

登录接口

  • 登录包含两种流程,实际上包括了注册和登录
  • 如果之前未使用该社交账号登录,则使用token调用开放api获取社交账号相关信息,注册并将结果返回
  • 如果之前已经使用该社交账号登录,则更新token并将结果返回
@RequestMapping("/oauth2/login")
public R login(@RequestBody SocialUser socialUser) {MemberEntity entity=memberService.login(socialUser);if (entity!=null){return R.ok().put("memberEntity",entity);}else {return R.error();}
}@Overridepublic MemberEntity login(SocialUser socialUser){MemberEntity uid = this.getOne(new QueryWrapper<MemberEntity>().eq("uid", socialUser.getUid()));//1 如果之前未登陆过,则查询其社交信息进行注册if (uid == null) {Map<String, String> query = new HashMap<>();query.put("access_token",socialUser.getAccess_token());query.put("uid", socialUser.getUid());//调用微博api接口获取用户信息String json = null;try {HttpResponse response = HttpUtils.doGet("https://api.weibo.com", "/2/users/show.json", "get", new HashMap<>(), query);json = EntityUtils.toString(response.getEntity());} catch (Exception e) {e.printStackTrace();}JSONObject jsonObject = JSON.parseObject(json);//获得昵称,性别,头像String name = jsonObject.getString("name");String gender = jsonObject.getString("gender");String profile_image_url = jsonObject.getString("profile_image_url");//封装用户信息并保存uid = new MemberEntity();MemberLevelEntity defaultLevel = memberLevelService.getOne(new QueryWrapper<MemberLevelEntity>().eq("default_status", 1));uid.setLevelId(defaultLevel.getId());uid.setNickname(name);uid.setGender("m".equals(gender)?0:1);uid.setHeader(profile_image_url);uid.setAccessToken(socialUser.getAccess_token());uid.setUid(socialUser.getUid());uid.setExpiresIn(socialUser.getExpires_in());this.save(uid);}else {//2 否则更新令牌等信息并返回uid.setAccessToken(socialUser.getAccess_token());uid.setUid(socialUser.getUid());uid.setExpiresIn(socialUser.getExpires_in());this.updateById(uid);}return uid;}

5. SpringSession

(1) session 原理

jsessionid相当于银行卡,存在服务器的session相当于存储的现金,每次通过jsessionid取出保存的数据

问题:但是正常情况下session不可跨域,它有自己的作用范围

在这里插入图片描述

(2) 分布式下session共享问题

在这里插入图片描述

(3) 解决方案
1) session复制

在这里插入图片描述

2) 客户端存储

在这里插入图片描述

3) hash一致性

在这里插入图片描述

4) 统一存储

在这里插入图片描述

(4) SpringSession整合redis

通过SpringSession修改session的作用域

在这里插入图片描述

1) 环境搭建

导入依赖

    <dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-data-redis</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>

修改配置

spring:redis:host: 192.168.56.102session:store-type: redis

添加注解

@EnableRedisHttpSession
public class GulimallAuthServerApplication {
2) 自定义配置
  • 由于默认使用jdk进行序列化,通过导入RedisSerializer修改为json序列化

  • 并且通过修改CookieSerializer扩大session的作用域至**.gulimall.com

@Configuration
public class GulimallSessionConfig {@Beanpublic RedisSerializer<Object> springSessionDefaultRedisSerializer() {return new GenericJackson2JsonRedisSerializer();}@Beanpublic CookieSerializer cookieSerializer() {DefaultCookieSerializer serializer = new DefaultCookieSerializer();serializer.setCookieName("GULISESSIONID");serializer.setDomainName("gulimall.com");return serializer;}
}
(5) SpringSession核心原理 - 装饰者模式
  • 原生的获取session时是通过HttpServletRequest获取的
  • 这里对request进行包装,并且重写了包装request的getSession()方法
@Override
protected void doFilterInternal(HttpServletRequest request,HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);//对原生的request、response进行包装SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(request, response, this.servletContext);SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(wrappedRequest, response);try {filterChain.doFilter(wrappedRequest, wrappedResponse);}finally {wrappedRequest.commitSession();}
}

购物车

1. 数据模型分析

(1) 数据存储

购物车是一个读多写多的场景,因此放入数据库并不合适,但购物车又是需要持久化,因此这里我们选用redis存储购物车数据。

(2) 数据结构

在这里插入图片描述

一个购物车是由各个购物项组成的,但是我们用List进行存储并不合适,因为使用List查找某个购物项时需要挨个遍历每个购物项,会造成大量时间损耗,为保证查找速度,我们使用hash进行存储

在这里插入图片描述

(3) VO编写

购物项vo

在这里插入图片描述

public class CartItemVo {private Long skuId;//是否选中private Boolean check = true;//标题private String title;//图片private String image;//商品套餐属性private List<String> skuAttrValues;//价格private BigDecimal price;//数量private Integer count;//总价private BigDecimal totalPrice;/*** 当前购物车项总价等于单价x数量* @return*/public BigDecimal getTotalPrice() {return price.multiply(new BigDecimal(count));}public void setTotalPrice(BigDecimal totalPrice) {this.totalPrice = totalPrice;}

购物车vo

在这里插入图片描述

public class CartVo {/*** 购物车子项信息*/List

相关文章:

谷粒商城—分布式高级②.md

认证服务 1. 环境搭建 创建gulimall-auth-server模块,导依赖,引入login.html和reg.html,并把静态资源放到nginx的static目录下 2. 注册功能 (1) 验证码倒计时 //点击发送验证码按钮触发下面函数 $("#sendCode").click(function () {//如果有disabled,说明最近…...

阿里云ECS命名规则解析与规格选型实战指南

阿里云ECS实例的命名规则通常采用 “ecs.{实例族}.{规格大小}” 的结构,各部分含义如下: 命名字段说明ecs代表“弹性计算服务”(Elastic Compute Service)。{实例族}标识实例的用途和代次(如 g7、c7、r7),由字母+数字组成。{规格大小}表示实例的资源配置(如 large、2xl…...

Spring MVC 的核心以及执行流程

Spring MVC的核心 Spring MVC是Spring框架中的一个重要模块&#xff0c;它采用了经典的MVC&#xff08;Model-View-Controller&#xff09;设计模式。 MVC是一种软件架构的思想&#xff0c;它将软件按照模型&#xff08;Model&#xff09;、视图&#xff08;View&#xff09;…...

ai json处理提示词

在解析JSON数据时&#xff0c;提示词的设计需要明确任务目标、输入格式以及期望的输出格式。以下是一些常用的提示词示例&#xff0c;适用于不同的JSON解析场景&#xff1a; 1. 提取特定字段 用于从JSON中提取特定字段的值。 示例&#xff1a; 从以下JSON数据中提…...

2025开源数据工程全景图

作者 | Alireza Sadeghi 译自Practical Data Engineering 2025年开源数据工程领域呈现蓬勃创新与生态重构的双重态势&#xff0c;九大技术赛道在实时化、轻量化与云原生架构驱动下加速演进。一份来自外网的2025年开源数据工程全景图全面地展示了这一领域的发展态势与走向&…...

438. 找到字符串中所有字母异位词(LeetCode 热题 100)

题目来源&#xff1a; 438. 找到字符串中所有字母异位词 - 力扣&#xff08;LeetCode&#xff09; 题目内容&#xff1a; 给定两个字符串 s 和 p&#xff0c;找到 s 中所有 p 的 异位词的子串&#xff0c;返回这些子串的起始索引。不考虑答案输出的顺序。 示例 1: 输入: s &…...

c++标准io与线程,互斥锁

封装一个 File 类&#xff0c; 用有私有成员 File* fp 实现以下功能 File f "文件名" 要求打开该文件 f.write(string str) 要求将str数据写入文件中 string str f.read(int size) 从文件中读取最多size个字节&#xff0c; 并将读取到的数据返回 析构函数 #…...

java简单实现请求deepseek

1.deepseek的api创建 deepseek官网链接 点击右上API开放平台后找到API keys 创建APIkey&#xff1a; 注意&#xff1a;创建好的apikey只能在创建时可以复制&#xff0c;要保存好 2.java实现请求deepseek 使用springbootmaven 2.1 pom文件&#xff1a; <?xml version&…...

Ext系列文件系统 -- 磁盘结构,磁盘分区,inode,ext文件系统,软硬链接

目录 1.理解硬盘 1.1 磁盘、服务器、机柜、机房 1.2 磁盘物理结构 1.3 磁盘的存储结构 1.4 磁盘的逻辑结构 1.4.1 理解逻辑结构 1.4.2 真实过程 1.5 CHS地址和LBA地址的相互转换 2.引入文件系统 2.1 “块”概念 2.2 “分区”概念 2.3 “inode”概念 3.ext2文件系…...

PyTorch Tensor 形状变化操作详解

PyTorch Tensor 形状变化操作详解 在深度学习中&#xff0c;Tensor 的形状变换是非常常见的操作。PyTorch 提供了丰富的 API 来帮助我们调整 Tensor 的形状&#xff0c;以满足模型输入、计算或数据处理的需求。本文将详细介绍 PyTorch 中常见的 Tensor 形状变换操作&#xff0…...

文字识别软件cnocr学习笔记

• 安装 pip install cnocr • 基础的使用方法 首次运行会下载安装模型&#xff0c;如果没有梯子&#xff0c;会报错&#xff1a; 在网络上查找cnocr的模型资源&#xff0c;并下载到本地。https://download.csdn.net/download/qq_33464428/89514689?ops_request_misc%257B%2…...

本地部署DeepSeek R1 + 界面可视化open-webui【ollama容器+open-webui容器】

本地部署DeepSeek R1 界面可视化open-webui 本文主要讲述如何用ollama镜像和open-webui镜像部署DeepSeek R1&#xff0c; 镜像比较方便我们在各个机器之间快速部署。 显卡推荐 模型版本CPU内存GPU显卡推荐1.5B4核8GB非必需4GBRTX1650、RTX20607B、8B8核16GB8GBRTX3070、RTX…...

macOS部署DeepSeek-r1

好奇&#xff0c;跟着网友们的操作试了一下 网上方案很多&#xff0c;主要参考的是这篇 DeepSeek 接入 PyCharm&#xff0c;轻松助力编程_pycharm deepseek-CSDN博客 方案是&#xff1a;PyCharm CodeGPT插件 DeepSeek-r1:1.5b 假设已经安装好了PyCharm PyCharm: the Pyth…...

基于STM32与BD623x的电机控制实战——从零搭建无人机/机器人驱动系统

系列文章目录 1.元件基础 2.电路设计 3.PCB设计 4.元件焊接 5.板子调试 6.程序设计 7.算法学习 8.编写exe 9.检测标准 10.项目举例 11.职业规划 文章目录 一、为什么选择这两个芯片&#xff1f;1.1 STM32微控制器1.2 ROHM BD623x电机驱动 二、核心控制原理详解2.1 H桥驱动奥…...

基于ffmpeg+openGL ES实现的视频编辑工具-字幕添加(六)

在视频编辑领域,字幕的添加是一项极为重要的功能,它能够极大地丰富视频内容,提升观众的观看体验。当我们深入探究如何实现这一功能时,FreeType 开源库成为了强大助力。本文将详细阐述借助 FreeType 库生成字幕数据的过程,以及如何实现字幕的缩放、移动、旋转、颜色修改、对…...

C++中const T为什么少见?它有什么用途?

在C中&#xff0c;右值引用&#xff08;T&&&#xff09;是移动语义和完美转发的核心特性之一&#xff0c;但你是否注意到&#xff0c;const T&&&#xff08;const右值引用&#xff09;却很少被使用&#xff1f;它到底有什么用途&#xff1f; 今天我们就来深入…...

Leetcode 位计算

3095. 或值至少 K 的最短子数组 I 3097. Shortest Subarray With OR at Least K II class Solution:def minimumSubarrayLength(self, nums: List[int], k: int) -> int:n len(nums)bits [0] * 30res infdef calc(bits):return sum(1 << i for i in range(30) if…...

SpringBoot3.x整合WebSocket

SpringBoot3.x整合WebSocket 本文主要介绍最新springboot3.x下如何整合WebSocket. WebSocket简述 WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议&#xff0c;它允许在浏览器和服务器之间进行实时的、双向的通信。相对于传统的基于请求和响应的 HTTP 协议&#xff…...

猿大师办公助手对比其他WebOffice在线编辑Office插件有什么优势

1. 原生Office功能完整嵌入&#xff0c;排版一致性保障 猿大师办公助手直接调用本地安装的微软Office、金山WPS或永中Office&#xff0c;支持所有原生功能&#xff08;如复杂公式、VBA宏等&#xff09;&#xff0c;确保网页编辑与本地打开的文档排版完全一致。 提供OLE嵌入和完…...

STM32创建静态库lib

创建静态库lib 1. 新建工程1.1 创建工程文件夹1.2 编写用户相关代码1.2.1 stm32f4xx_it.h1.2.2 stm32f4xx_it.c1.2.3 标准库配置&#xff1a;stm32f4xx_conf.h1.2.4 HAL库的配置&#xff1a;stm32f4xx_hal_conf.h1.2.5 LL库配置&#xff1a;stm32f4xx_ll_conf.h 1.3 移植通用文…...

炉石传说脚本完整指南:如何轻松实现自动对战与卡组管理

炉石传说脚本完整指南&#xff1a;如何轻松实现自动对战与卡组管理 【免费下载链接】Hearthstone-Script Hearthstone script&#xff08;炉石传说脚本&#xff09; 项目地址: https://gitcode.com/gh_mirrors/he/Hearthstone-Script 炉石传说脚本是一款专为《炉石传说》…...

DeepSeek V4论文降AI干货,2026年4月10个实用技巧

DeepSeek V4 在 2026-04-24 正式上线&#xff0c;写论文的速度比 V3 又快了一截&#xff0c;但随之而来的麻烦也很现实&#xff1a;AI 检测率比上一代更高。我自己在 04-24 当晚拿一篇用 V4 生成的开题报告去测&#xff0c;知网 AIGC 疑似 67.4%&#xff0c;维普 58%&#xff0…...

HyperAgent开源框架:构建AI智能体的状态管理与工具集成实践

1. 项目概述&#xff1a;一个面向AI智能体的开源框架最近在折腾AI智能体&#xff08;Agent&#xff09;相关的项目&#xff0c;发现了一个挺有意思的开源框架——HyperAgent。这名字听起来就挺“超”的&#xff0c;HyperBrowserAI团队出品。简单来说&#xff0c;它不是一个具体…...

电影票特惠出票和快速出票到底什么逻辑? 看完就懂!

两种出票方式的底层逻辑完全不一样打开宜选影票选座购票&#xff0c;总能在确认页看到特惠出票和快速出票两个选项。哪怕座位一模一样&#xff0c;两个按钮背后走的流程&#xff0c;差得可不是一星半点。很多人以为只是平台分了两个通道赚差价&#xff0c;其实真不是这么简单。…...

Pomotroid番茄工作法计时器:如何用这个免费工具快速提升专注力

Pomotroid番茄工作法计时器&#xff1a;如何用这个免费工具快速提升专注力 【免费下载链接】pomotroid :tomato: Simple and visually-pleasing Pomodoro timer 项目地址: https://gitcode.com/gh_mirrors/po/pomotroid 想要告别拖延、提升工作效率&#xff1f;Pomotroi…...

【VSCode 2026远程同步终极指南】:3大底层协议重构+毫秒级差异检测,98.7%开发者尚未启用的隐藏同步加速模式

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;VSCode 2026远程同步架构演进全景图 VSCode 2026 的远程同步能力已从早期的 SSH 隧道代理模式&#xff0c;全面升级为基于轻量级服务网格&#xff08;Service Mesh Lite&#xff09;与端到端加密状态快…...

为什么 99% 的定位系统都是错的?镜像视界(浙江)给出唯一答案核心定论

为什么 99% 的定位系统都是错的&#xff1f;镜像视界&#xff08;浙江&#xff09;给出唯一答案核心定论&#xff1a;99% 定位系统的底层错误&#xff0c;是依赖硬件标签、信号估算与图像猜测&#xff0c;无法进入真实三维空间&#xff1b;镜像视界&#xff08;浙江&#xff09…...

UnityFigmaBridge终极指南:从设计到开发的完整高效协作方案

UnityFigmaBridge终极指南&#xff1a;从设计到开发的完整高效协作方案 【免费下载链接】UnityFigmaBridge Easily bring your Figma Documents, Components, Assets and Prototypes to Unity 项目地址: https://gitcode.com/gh_mirrors/un/UnityFigmaBridge 你是否曾为…...

机器学习交叉验证优化7大实战技巧

1. 交叉验证优化入门&#xff1a;从基础到进阶交叉验证是机器学习模型评估的黄金标准&#xff0c;但很多从业者只停留在基础用法。我在实际项目中发现&#xff0c;合理优化交叉验证流程可以使模型评估效率提升3-5倍&#xff0c;同时获得更可靠的性能指标。让我们从一个基础示例…...

FanControl深度技术解析:基于插件架构的Windows散热控制系统优化方案

FanControl深度技术解析&#xff1a;基于插件架构的Windows散热控制系统优化方案 【免费下载链接】FanControl.Releases This is the release repository for Fan Control, a highly customizable fan controlling software for Windows. 项目地址: https://gitcode.com/GitH…...