当前位置: 首页 > 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 移植通用文…...

golang循环变量捕获问题​​

在 Go 语言中&#xff0c;当在循环中启动协程&#xff08;goroutine&#xff09;时&#xff0c;如果在协程闭包中直接引用循环变量&#xff0c;可能会遇到一个常见的陷阱 - ​​循环变量捕获问题​​。让我详细解释一下&#xff1a; 问题背景 看这个代码片段&#xff1a; fo…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建

制造业采购供应链管理是企业运营的核心环节&#xff0c;供应链协同管理在供应链上下游企业之间建立紧密的合作关系&#xff0c;通过信息共享、资源整合、业务协同等方式&#xff0c;实现供应链的全面管理和优化&#xff0c;提高供应链的效率和透明度&#xff0c;降低供应链的成…...

Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器

第一章 引言&#xff1a;语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域&#xff0c;文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量&#xff0c;支撑着搜索引擎、推荐系统、…...

【Go】3、Go语言进阶与依赖管理

前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课&#xff0c;做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程&#xff0c;它的核心机制是 Goroutine 协程、Channel 通道&#xff0c;并基于CSP&#xff08;Communicating Sequential Processes&#xff0…...

TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案

一、TRS收益互换的本质与业务逻辑 &#xff08;一&#xff09;概念解析 TRS&#xff08;Total Return Swap&#xff09;收益互换是一种金融衍生工具&#xff0c;指交易双方约定在未来一定期限内&#xff0c;基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...

NFT模式:数字资产确权与链游经济系统构建

NFT模式&#xff1a;数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新&#xff1a;构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议&#xff1a;基于LayerZero协议实现以太坊、Solana等公链资产互通&#xff0c;通过零知…...

[Java恶补day16] 238.除自身以外数组的乘积

给你一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&#xff0c;且在 O(n) 时间复杂度…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)

本期内容并不是很难&#xff0c;相信大家会学的很愉快&#xff0c;当然对于有后端基础的朋友来说&#xff0c;本期内容更加容易了解&#xff0c;当然没有基础的也别担心&#xff0c;本期内容会详细解释有关内容 本期用到的软件&#xff1a;yakit&#xff08;因为经过之前好多期…...

20个超级好用的 CSS 动画库

分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码&#xff0c;而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库&#xff0c;可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画&#xff0c;可以包含在你的网页或应用项目中。 3.An…...

深度学习水论文:mamba+图像增强

&#x1f9c0;当前视觉领域对高效长序列建模需求激增&#xff0c;对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模&#xff0c;以及动态计算优势&#xff0c;在图像质量提升和细节恢复方面有难以替代的作用。 &#x1f9c0;因此短时间内&#xff0c;就有不…...