【雷丰阳-谷粒商城 】【分布式高级篇-微服务架构篇】【21】【购物车】


持续学习&持续更新中…

守破离


【雷丰阳-谷粒商城 】【分布式高级篇-微服务架构篇】【21】【购物车】

  • 购物车需求描述
  • 购物车数据结构
  • 数据Model抽取
  • 实现流程(参照京东)
  • 代码实现
  • 参考

购物车需求描述

  • 用户可以在登录状态下将商品添加到购物车【用户购物车/在线购物车】

    • 放入数据库
    • mongodb
    • 放入 redis(采用)
    • 登录以后,会将临时购物车的数据全部合并过来,并清空临时购物车
  • 用户可以在未登录状态下将商品添加到购物车【游客购物车/离线购物车/临时购物车】

    • 放入 localstorage
    • cookie
    • WebSQL
    • (客户端存储,后台不存,后台就没法分析用户的购物偏好)
    • 放入 redis(采用,有价值的数据要放在后端存储,便于大数据分析)
    • 浏览器即使关闭,下次进入,临时购物车数据都在
  • 用户可以使用购物车一起结算下单

  • 给购物车添加商品

  • 用户可以查询自己的购物车

  • 用户可以在购物车中修改购买商品的数量。

  • 用户可以在购物车中删除商品。

  • 保存选中不选中商品的状态

  • 在购物车中展示商品优惠信息

  • 提示购物车商品价格变化

注意:真实开发中,最好有一个Redis(集群) 专门负责购物车,不应该跟负责缓存的Redis混合起来使用

购物车数据结构

在这里插入图片描述

每一个购物项信息,都是一个对象,基本字段包括:

{skuId: 2131241, check: true, title: "Apple iphone.....", defaultImage: "...", price: 4999, count: 1, totalPrice: 4999, skuSaleVO: {...} 
}

购物车中不止一条数据,因此最终会是对象的数组:

[{},{},...
]

Redis 有 5 种不同数据结构,这里选择哪一种比较合适呢?

  • 首先不同用户应该有独立的购物车,因此购物车应该以用户作为 key 来存储,Value 是 用户的购物车(所有购物项)信息。这样看来基本的k-v结构就可以了。

  • 但是,我们对购物车中的商品进行增、删、改操作,基本都需要根据商品 id 进行判断, 为了方便后期处理,购物车里面也应该是k-v结构,key 是商品 id,value 是这个商品的购物项信息。

  • 综上所述,我们的购物车结构是一个双层Map:Map<String, Map<String, CartItemInfo>> 在这里插入图片描述

  • 第一层 Map,Key 是用户 id ,Value 是用户对应的购物车

  • 第二层 Map,Key 是购物车中的商品 id,Value 是对应商品的购物项信息

在这里插入图片描述

Map<String k1, Map<String k2, CartItemInfo item> userCart>

  • k1:标识每一个用户的购物车
  • k2:购物项的商品id

在Redis中

  • key:用户标识
  • value:Hash(k:商品id,v:购物项详情)

数据Model抽取

/*** 整个购物车* 需要计算的属性,必须重写他的get方法,保证每次获取属性都会进行计算*/
public class Cart {List<CartItem> items;private Integer countNum;//商品数量private Integer countType;//商品类型数量private BigDecimal totalAmount;//商品总价private BigDecimal reduce = new BigDecimal("0.00");//减免价格public List<CartItem> getItems() {return items;}public void setItems(List<CartItem> items) {this.items = items;}public Integer getCountNum() {int count = 0;if (items != null && items.size() > 0) {for (CartItem item : items) {count += item.getCount();}}return count;}public Integer getCountType() {int count = 0;if (items != null && items.size() > 0) {for (CartItem item : items) {count += 1;}}return count;}public BigDecimal getTotalAmount() {BigDecimal amount = new BigDecimal("0");//1、计算购物项总价if (items != null && items.size() > 0) {for (CartItem item : items) {if(item.getCheck()){BigDecimal totalPrice = item.getTotalPrice();amount = amount.add(totalPrice);}}}//2、减去优惠总价BigDecimal subtract = amount.subtract(getReduce());return subtract;}public BigDecimal getReduce() {return reduce;}public void setReduce(BigDecimal reduce) {this.reduce = reduce;}
}
/*** 购物项内容*/
public class CartItem {private Long skuId;private Boolean check = true;private String title;private String image;private List<String> skuAttr;private BigDecimal price;private Integer count;private BigDecimal totalPrice;public Long getSkuId() {return skuId;}public void setSkuId(Long skuId) {this.skuId = skuId;}public Boolean getCheck() {return check;}public void setCheck(Boolean check) {this.check = check;}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}public String getImage() {return image;}public void setImage(String image) {this.image = image;}public List<String> getSkuAttr() {return skuAttr;}public void setSkuAttr(List<String> skuAttr) {this.skuAttr = skuAttr;}public BigDecimal getPrice() {return price;}public void setPrice(BigDecimal price) {this.price = price;}public Integer getCount() {return count;}public void setCount(Integer count) {this.count = count;}/*** 计算当前项的总价* @return*/public BigDecimal getTotalPrice() {return this.price.multiply(new BigDecimal("" + this.count));}public void setTotalPrice(BigDecimal totalPrice) {this.totalPrice = totalPrice;}
}

Redis本来就是<key,value>结构

所以,我们只需要BoundHashOperations<String, Object, Object> hashOperations = stringRedisTemplate.boundHashOps(CartConstant.CART_REDIS_KEY_PREFIX + key);,即可从Redis中获取一个类似于HashMap的对象,充当用户的购物车

保存购物项就可以这样写:hashOperations .put(skuId.toString(), JSON.toJSONString(cartItem));

获取购物项可以这样写:CartItem cartItem = JSON.parseObject(hashOperations.get(skuId.toString()), CartItem.class);

实现流程(参照京东)

在这里插入图片描述

user-key 是随机生成的 id,不管有没有登录都会有这个 cookie 信息。

  • 浏览器有一个cookie;user-key;标识用户身份,一个月后过期;
  • 如果第一次使用jd的购物车功能,都会给一个临时的用户身份;user-key 这个 Cookie
  • 浏览器以后保存,每次访问都会带上这个cookie;
  • 登录:session中有用户信息
  • 没登录:按照cookie里面带来的user-key来做
  • 第一次使用购物车页面:如果没有临时用户user-key,就帮忙创建一个临时用户user-key。

ThreadLocal—同一个线程共享数据:(Map<Thread,Object> threadLocal )

在这里插入图片描述

@ToString
@Data
public class UserInfoTo {private Long userId;private String userKey; //一定会封装,user-key 是随机生成的 id,不管有没有登录都会有这个 cookie 信息。private boolean flag = false; // 只需要让浏览器保存一次user-key这个cookie即可
}
public class CartConstant {public static final String TEMP_USER_COOKIE_NAME = "user-key";public static final int TEMP_USER_COOKIE_TIMEOUT = 60*60*24*30; // Cookie的有效期 一个月后过期
}
/*** 判断用户的登录状态。并封装传递(用户信息)给 controller。命令浏览器保存user-key这个Cookie*/
public class GulimallCartInterceptor implements HandlerInterceptor {//   ThreadLocal: 同一个线程共享数据,可以让Controller等,快速得到用户信息UserInfoTopublic static final ThreadLocal<UserInfoTo> THREAD_LOCAL = new ThreadLocal<>();/*** 目标方法执行之前*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {UserInfoTo userInfoTo = new UserInfoTo();//        request是SpringSession已经包装过的MemberRespVo member = (MemberRespVo) request.getSession().getAttribute(AuthServerConstant.LOGIN_USER);if (null != member) { // 登录了userInfoTo.setUserId(member.getId());
//            利用GULISESSION这个Cookie(SpringSession配置的),也就是sessionId作为user-key合适吗?
//            不合适
//            虽然能判断并获取这个信息,但是你使用了认证服务的信息,符合微服务分模块开发吗?
//            而且这样用的话,会增加复杂性
//            比如在用户没有登陆的情况下,userInfoTo的user-key一定会被设置过了,并且浏览器也保存了这个user-key
//            然后用户再次登录,不能将这个信息作为user-key直接使用了,又得给UserInfoTo再增添一个字段,比如叫userSessionId,反正很麻烦
//            Cookie[] cookies = request.getCookies();
//            if (cookies != null && cookies.length > 0) {
//                for (Cookie cookie : cookies) {
//                    if (cookie.getName().equals("GULISESSION")) {
//                        System.out.println(cookie.getName() + "====>" + cookie.getValue());
//                        break;
//                    }
//                }
//            }}//        判断浏览器有没有带来user-key这个CookieCookie[] cookies = request.getCookies();if (cookies != null && cookies.length > 0) {for (Cookie cookie : cookies) {
//                  浏览器有带来user-key这个cookie(不是第一次使用购物车页面)if (cookie.getName().equals(CartConstant.TEMP_USER_COOKIE_NAME)) {userInfoTo.setUserKey(cookie.getValue());// 浏览器已经保存了user-key这个Cookie,那么就不需要浏览器再次保存了,如果不设置,那么这个Cookie会无限续期userInfoTo.setFlag(true);break;}}}//        只要没有user-key,不管你有没有登录,就代表是第一次使用购物车页面,都给你生成一个user-keyif (StringUtils.isEmpty(userInfoTo.getUserKey())) { // 是第一次使用购物车页面String userKey = UUID.randomUUID().toString();userInfoTo.setUserKey(userKey);}THREAD_LOCAL.set(userInfoTo);return true;}/*** 业务执行之后;分配临时用户,让浏览器保存user-key*/@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {UserInfoTo userInfoTo = THREAD_LOCAL.get();//如果没有临时用户一定要让浏览器保存一个临时用户if (!userInfoTo.isFlag()) {Cookie cookie = new Cookie(CartConstant.TEMP_USER_COOKIE_NAME, userInfoTo.getUserKey());cookie.setMaxAge(CartConstant.TEMP_USER_COOKIE_TIMEOUT);cookie.setDomain("gulimall.com");response.addCookie(cookie); // 让浏览器保存user-key这个Cookie}}}
@Configuration
public class GulimallCartWebConfig implements WebMvcConfigurer {/*** 添加拦截器*/@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new GulimallCartInterceptor()).addPathPatterns("/**");}
}

代码实现

两个功能比较重要:新增商品到购物车、查询购物车。

新增商品:判断是否登录

  • 是:则添加商品到后台 Redis 中,把 user 的唯一标识符作为 key。
  • 否:则添加商品到后台 redis 中,使用随机生成的 user-key 作为 key。
  • 购物车里有该商品,更改数量即可
  • 购物车里没有该商品, 添加新商品到购物车

查询购物车列表:判断是否登录

  • 否:直接根据 user-key 查询 redis 中数据并展示
  • 是:已登录,则需要先根据 user-key 查询 redis 是否有数据。
    • 有:合并离线购物车数据到登录用户的购物车,而后查询 redis。
    • 否:直接去后台查询 redis,而后返回。
    private BoundHashOperations<String, Object, Object> getCurrentUserCartHashOps() {UserInfoTo userInfoTo = GulimallCartInterceptor.THREAD_LOCAL.get();String userKey = userInfoTo.getUserKey();Long userId = userInfoTo.getUserId();BoundHashOperations<String, Object, Object> hashOperations;if (userId != null) { // 登录了,操作登录购物车hashOperations = stringRedisTemplate.boundHashOps(CartConstant.CART_REDIS_KEY_PREFIX + userId);} else { // 没登陆,操作离线购物车hashOperations = stringRedisTemplate.boundHashOps(CartConstant.CART_REDIS_KEY_PREFIX + userKey);}return hashOperations;}private List<CartItem> listCartItems(Object userInfoKey) {BoundHashOperations<String, Object, Object> ops = stringRedisTemplate.boundHashOps(CartConstant.CART_REDIS_KEY_PREFIX + userInfoKey);List<Object> values = ops.values();if (null != values && values.size() > 0)return values.stream().map(item -> JSON.parseObject(item.toString(), CartItem.class)).collect(Collectors.toList());return null;}

添加商品到购物车:

    @Overridepublic CartItem addToCart(Long skuId, Integer num) throws ExecutionException, InterruptedException {BoundHashOperations<String, Object, Object> cartOps = getCurrentUserCartHashOps();Object o = cartOps.get(skuId.toString());if (o != null) {// 购物车里有该商品,更改数量即可String cartItemJSON = o.toString();CartItem cartItem = JSON.parseObject(cartItemJSON, CartItem.class);cartItem.setCount(cartItem.getCount() + num);cartOps.put(skuId.toString(), JSON.toJSONString(cartItem));return cartItem;} else {// 购物车里没有该商品 添加新商品到购物车CartItem cartItem = new CartItem();//1、远程查询当前要添加的商品的信息CompletableFuture<Void> getSkuInfoTask = CompletableFuture.runAsync(() -> {R skuInfo = productFeignService.getSkuInfo(skuId);SkuInfoVo data = skuInfo.getData("skuInfo", new TypeReference<SkuInfoVo>() {});cartItem.setCheck(true);cartItem.setCount(num);cartItem.setImage(data.getSkuDefaultImg());cartItem.setTitle(data.getSkuTitle());cartItem.setSkuId(skuId);cartItem.setPrice(data.getPrice());}, executor);//2、远程查询sku的组合信息CompletableFuture<Void> getSkuSaleAttrValues = CompletableFuture.runAsync(() -> {List<String> values = productFeignService.getSkuSaleAttrValues(skuId);cartItem.setSkuAttr(values);}, executor);CompletableFuture.allOf(getSkuInfoTask, getSkuSaleAttrValues).get();String s = JSON.toJSONString(cartItem);cartOps.put(skuId.toString(), s);return cartItem;}}

获取用户的购物车:

    @Overridepublic Cart getCart() throws ExecutionException, InterruptedException {UserInfoTo userInfoTo = GulimallCartInterceptor.THREAD_LOCAL.get();Long userId = userInfoTo.getUserId();String userKey = userInfoTo.getUserKey();Cart cart = new Cart();if (userId != null) {// 如果用户有离线购物车,需要合并离线购物车到登录购物车,并且清空临时购物车List<CartItem> tempCartItems = listCartItems(userKey);if (null != tempCartItems && tempCartItems.size() > 0) {for (CartItem tempCartItem : tempCartItems) {addToCart(tempCartItem.getSkuId(), tempCartItem.getCount()); // 合并离线购物车的购物项到登录购物车}CompletableFuture.runAsync(() -> {stringRedisTemplate.delete(CartConstant.CART_REDIS_KEY_PREFIX + userKey); // 清空临时购物车}, executor);}// 登录了,展示登录购物车List<CartItem> loginCartItems = listCartItems(userId);cart.setItems(loginCartItems);} else {// 没登陆,展示离线购物车List<CartItem> tempCartItems = listCartItems(userKey);cart.setItems(tempCartItems);}return cart;}

controller使用:

   /*** 浏览器有一个cookie;user-key;标识用户身份,一个月后过期;* 如果第一次使用jd的购物车功能,都会给一个临时的用户身份;user-key这个Cookie* 浏览器以后保存,每次访问都会带上这个cookie;* <p>* 登录:session有用户信息* 没登录:按照cookie里面带来user-key来做* 第一次使用购物车页面:如果没有临时用户user-key,帮忙创建一个临时用户user-key。*/@GetMapping("/cart.html")public String cartListPage(Model model) throws ExecutionException, InterruptedException {
//        Cart cart = cartService.getCart();
//        model.addAttribute("cart",cart);
//        UserInfoTo userInfoTo = GulimallCartInterceptor.THREAD_LOCAL.get();
//        System.out.println("CartController ===> " + userInfoTo);Cart cart = cartService.getCart();model.addAttribute("cart", cart);return "cartList";}/*** 添加商品到购物车* http://cart.gulimall.com/addToCart?skuId=1&num=1* * RedirectAttributes ra*      ra.addFlashAttribute();将数据放在session里面可以在页面取出,但是只能取一次*      ra.addAttribute("skuId",skuId);将数据放在url后面*/@GetMapping("/addToCart")public String addToCart(@RequestParam("skuId") Long skuId, @RequestParam("num") Integer num, RedirectAttributes redirectAttributes) throws ExecutionException, InterruptedException {CartItem cartItem = cartService.addToCart(skuId, num);redirectAttributes.addAttribute("skuId", skuId);return "redirect:http://cart.gulimall.com/addToCartSuccess"; //重定向到成功页面,防止用户刷新页面再次提交数据添加到购物车}/*** 跳转到成功页*/@GetMapping("/addToCartSuccess")public String addToCartSuccess(@RequestParam("skuId") Long skuId, Model model) {//添加商品到购物车成功,再次查询购物车数据即可CartItem item = cartService.getCartItem(skuId);model.addAttribute("item", item);return "success";}

参考

雷丰阳: Java项目《谷粒商城》Java架构师 | 微服务 | 大型电商项目.


本文完,感谢您的关注支持!


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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

读人工智能全传05专家系统

1. 知识就是力量 1.1. 人工智能领域此前存在的问题是过度关注搜索和解决问题这种通用法则 1.2. “弱”方法缺少一个关键的要素&#xff0c;而这一要素才是在所有智能行为中起决定性作用的组成部分&#xff1a;知识 1.3. 一种基于知识的人工智能系统&#xff1a;专家系统开始…

2024年第十四届亚太地区大学生数学建模竞赛(中文赛项)B题洪水灾害的数据分析与预测论文和代码分析

经过不懈的努力&#xff0c; 2024年第十四届亚太地区大学生数学建模竞赛&#xff08;中文赛项&#xff09;B题洪水灾害的数据分析与预测论文和代码已完成&#xff0c;代码为C题全部问题的代码&#xff0c;论文包括摘要、问题重述、问题分析、模型假设、符号说明、模型的建立和求…

python函数和c的区别有哪些

Python有很多内置函数&#xff08;build in function&#xff09;&#xff0c;不需要写头文件&#xff0c;Python还有很多强大的模块&#xff0c;需要时导入便可。C语言在这一点上远不及Python&#xff0c;大多时候都需要自己手动实现。 C语言中的函数&#xff0c;有着严格的顺…

Excel 宏录制与VBA编程 ——VBA编程技巧篇三 (未初始化Range判断、遍历工作表方法、工作表多行重复内容剔除)

未初始化Range的判断 有时候需要对已定义未初始化的range对象做判断 dim curRange as range If curRange Is Nothing Thendebug.print("未初始化的..") End If遍历工作表方法 Chr&#xff08;10&#xff09;&#xff1a;ASCII码中的换行键&#xff0c;相当于vbLF。 …

昇思MindSpore学习笔记4-04生成式--GAN图像生成

摘要&#xff1a; 记录昇思MindSpore AI框架使用GAN生成式对抗网络模型生成图像的原理和实际使用方法、步骤。包括环境准备、数据集下载、数据加载、隐码、构建生成器与判别器、模型训练、模型推理等。 一、概念 GAN生成式对抗网络模型 (Generative Adversarial Networks) …

【Python学习笔记】菜鸟教程Scrapy案例 + B站amazon案例视频

背景前摇&#xff08;省流可以跳过这部分&#xff09; 实习的时候厚脸皮请教了一位办公室负责做爬虫这块的老师&#xff0c;给我推荐了Scrapy框架。 我之前学过一些爬虫基础&#xff0c;但是用的是比较常见的BeautifulSoup和Request&#xff0c;于是得到Scrapy这个关键词后&am…

分子AI预测赛Task4笔记(结束)

话不多说&#xff0c;直接上官方链接&#xff1a;‌​​​‍&#xfeff;​⁠​‌​‍​​&#xfeff;​‌​⁠‬​&#xfeff;‬​​‌​​​​‬‬​​​​‍⁠‍‌​&#xfeff;⁠Task3&#xff1a;进阶baseline详解 - 飞书云文档 (feishu.cn)Task4&#xff1a;持续尝试&…

51单片机STC89C52RC——16.1 五项四线步进电机

目的/效果 让步进电机 正向转90度&#xff0c;逆向转90度 一&#xff0c;STC单片机模块 二&#xff0c;步进电机 2.2 什么是步进电机&#xff1f; 步进电机可以理解为&#xff1a;是一个按照固定步幅运动的“小型机器”。它与普通电机不同点在于&#xff0c;普通电机可以持…

【2024_CUMCM】时间序列算法ARMA

目录 2023-c-问题二 问题分析 介绍 单位根检验 白噪声检验 自相关和偏自相关图 利用信息准则定阶 构建AMIMA模型 D-W检验 预测 代码 2023-c-问题二 问题分析 ARMA适合多个领域的时间序列分析&#xff0c;不同时间的定价策略属于这类问题。 介绍 ARMA模型&…

昇思25天学习打卡营第14天|基于MindNLP的文本解码原理

基于MindNLP的文本解码原理 文本解码 文本解码是自然语言处理中的一个关键步骤,特别是在任务如机器翻译、文本摘要、自动回复生成等领域。解码过程涉及将编码器(如语言模型、翻译模型等)的输出转换为可读的文本序列。以下是一些常见的文本解码方法和原理: 1. 自回归解码:…

10 docker 安装 mysql详解

目录 一、安装mysql 1. 镜像获取 2. 简单版 2.1. 使用mysql镜像 2.2. 建库建表插入数据 2.3. 外部win 连接并插入中文数据测试 2.4. 简单版本问题 3. 创建容器-实战版 3.1. 新建容器实列并挂载卷 3.2. 新建my.cnf , 解决中文乱码 3.3. 重启容器验证 3.4. 总结 一、…

BigDecimal(double)和BigDecimal(String)有什么区别?BigDecimal如何精确计数?

BigDecimal(double)和BigDecimal(String)的区别 double是不精确的&#xff0c;所以使用一个不精确的数字来创建BigDecimal&#xff0c;得到的数字也是不精确的。如0.1这个数字&#xff0c;double只能表示他的近似值。所以&#xff0c;当我们使用new BigDecimal(0.1)创建一个Bi…

Report Design Analysis报告之logic level详解

目录 一、前言 二、Logic Level distribution 2.1 logic level配置 2.2 Logic Level Distribution报告 2.3 Logic Level 报告详情查看 2.4 Route Distributions 报告详情查看 2.5 示例代码 一、前言 ​在工程设计中&#xff0c;如果需要了解路径的逻辑级数&#xff0c;可…

【python技巧】parser传入参数

参考网址: https://lightning.ai/docs/pytorch/LTS/api/pytorch_lightning.utilities.argparse.html#pytorch_lightning.utilities.argparse.add_argparse_args 1. 简单传入参数. parse_known_args()方法的作用就是把不在预设属性里的参数也返回,比如下面这个例子, 执行pytho…

2024吉他手的超级助手Guitar Pro8中文版本发布啦!

亲爱的音乐爱好者们&#xff0c;今天我要来和你们分享一款让我彻底沉迷的软件—Guitar Pro。如果你是一名热爱吉他的朋友&#xff0c;那么接下来的内容你可要瞪大眼睛仔细看哦&#xff01;&#x1f440;&#x1f3b6; Guitar Pro免费绿色永久安装包下载&#xff1a;&#xff0…

搭建NEMU与QEMU的DiffTest环境(动态库方式)

搭建NEMU与QEMU的DiffTest环境&#xff08;动态库方式&#xff09; 1 DiffTest原理简述2 编译NEMU3 编译qemu-dl-difftest3.1 修改NEMU/scripts/isa.mk3.2 修改NEMU/tools/qemu-dl-diff/src/diff-test.c3.3 修改NEMU/scripts/build.mk3.4 让qemu-dl-difftest带调试信息3.5 编译…

React中的useMemo和memo

引言 React是一个声明式的JavaScript库&#xff0c;用于构建用户界面。在开发过程中&#xff0c;性能优化是一个重要的方面。useMemo和memo是React提供的工具&#xff0c;用于帮助开发者避免不必要的渲染和计算&#xff0c;从而提升应用性能。 问题背景 在React应用中&#…

Ubuntu编译 OSG

目录 一、安装步骤 二、配置 1、数据文件配置 2、OSG环境变量配置 一、安装步骤 在Ubuntu上安装OSG(OpenSceneGraph),你可以按照以下步骤操作: 打开终端,更新你的包管理器的包列表: sudo apt update 安装必要的依赖库 sudo apt install libglu1-mesa-dev freeglu…

数字信号处理及MATLAB仿真(3)——量化的其他概念

上回书说到AD转换的两个步骤——量化与采样两个步骤。现在更加深入的去了解以下对应的概念。学无止境&#xff0c;要不断地努力才有好的收获。万丈高楼平地起&#xff0c;唯有打好基础&#xff0c;才能踏实前行。 不说了&#xff0c;今天咱们继续说说这两个步骤&#xff0c;首先…

[激光原理与应用-101]:南京科耐激光-激光焊接-焊中检测-智能制程监测系统IPM介绍 - 5 - 3C行业应用 - 电子布局类型

目录 前言&#xff1a; 一、激光在3C行业的应用概述 1.1 概述 1.2 激光焊接在3C-电子行业应用 二、3C电子行业中激光焊接 2.1 纽扣电池 2.2 均温板 2.3 指纹识别器 2.4 摄像头模组 2.5 IC芯片切割 三、3C行业中激光切割 四、激光在3C行业中的其他应用 4.1 涂层去除…