2023最新谷粒商城笔记之订单服务篇(全文总共13万字,超详细)
淘宝搜:【天降红包222】领超级红包,京东搜:【天降红包222】
淘宝互助,淘宝双11微信互助群关注公众号 【淘姐妹】
在服务器的 路径下创建一个 order 文件夹,在order路径下分别创建以下几个文件夹,用来存放对应的静态资源
-
detail 文件夹下存放 等待付款的静态资源, 并将等待付款文件夹下的页面复制到 【【微信】】服务中并命名为
-
list 文件夹下存放 订单页的静态资源,并将订单页文件夹下的页面复制到 【【微信】】服务中并命名为
-
confirm 文件夹下存放 结算页的静态资源,并将结算页文件夹下的页面复制到 【【微信】】服务中并命名为
-
pay 文件夹下存放 收银页的静态资源,并将收银页文件夹下的页面复制到 【【微信】】服务中并命名为
- 修改文件,添加新的域名
- 配置网关路由 【【淘密令】】-gateway
将订单服务注册到注册中心去:
-
导入Nacos依赖
-
主启动类加上 注解
-
配置注册中心信息
-
导入 thymeleaf的依赖并在开发期间禁用缓存
- 配置清除缓存
第一步、 导入依赖
第二步、 修改配置
主启动类上添加【【微信】】自动启动的注解
第三步、 导入【【微信】】、线程池配置类
-
添加【【微信】】的配置,添加“com.【【微信】】limall【【网址】】nfi【【淘密令】】SessionConfig”类,代码如下
-
添加线程池的配置,添加“com.【【微信】】limall【【网址】】nfig.MyThreadConfig”类,代码如下
-
线程池配置需要的属性
添加“com.【【微信】】limall【【网址】】nfig.ThreadPoolConfigProperties”类,代码如下
第四步、 页面调整
- 修改商城首页、商品页我的订单地链接地址
- 获取用户信息
- 代付款
- 已付款/待发货
- 待收货/已发货
- 已完成
- 已取消
- 售后中
订单生成 -> 支付订单 -> 卖家发货 -> 确认收货 -> 交易成功
需求:去结算、查看订单必须是登录用户之后才能进行的,这里编写一个拦截器。
-
用户登录则放行请求
-
用户未登录则跳转到登录页面进行登录
修改cartList.html 页面的**“去结算”**的链接地址
修改【【淘密令】】-auth-server的login.html页面接收提醒信息,将未登录消息进行提醒回显
【【微信】】服务中 路径下
【【微信】】服务中 路径下
【【微信】】服务中 路径下,需要配置以下配置类拦截器才会生效:
分析订单确认页得到订单确认页需要用到的数据,此数据需要传递给后端,因此我们需要将其封装为一个VO
- 因为存在网路延迟等问题,若一直点下单会下许多。所以我们需要防重令牌
【【微信】】 服务中 路径下 VO类:
收货地址,ums_member_recei【【微信】】 表
商品项信息
【【微信】】 订单确认页数据获取接口编写
- Controller 层方法编写 Gulimall-product 服务中 路径下 OrderWebController类
-
Service层实现类方法编写
- 1、远程查询所有的地址列表
- 2、远程查询购物车所有选中的购物项
- 3、查询用户积分
- 4、其他数据自动计算
- 5、防重令牌
Gulimall-product 服务中 路径下 OrderSer【【微信】】
编写Gulimall-common获取会员所有收货地址接口
-
编写Controller层接口方法 Gulimall-member 服务中路径下 MemberRecei【【微信】】 类
-
Service层实现类 编写 获取会有收货地址列表 方法 Gulimall-member 服务中路径下 MemberReceiveAddressSer【【微信】】 实现类
-
【【微信】】服务中编写远程调用 【【淘密令】】-member服务 feign接口 【【微信】】服务中 路径下 MemberFeignService 接口
编写【【微信】】 购物车服务中用户选择的所有购物项
- 首先通过用户ID在Redis中查询到购物车中的所有的购物项
- 通过 filter 过滤 用户购物车中被选择的购物项
- 查询数据库中当前购物项的价格,不能使用之前加入购物车的价格
- 编写远程 【【淘密令】】-product 服务中的 查询sku价格接口
第一步、编写Controller层接口
编写 【【淘密令】】-cart 服务中 路径下的 CartController 类:
第二步、Service层实现类 获取用户选择的所有购物项方法编写
编写 【【淘密令】】-cart 服务中 路径中 CartSer【【微信】】 类
第三步、编写Gulimall-product 服务中获取指定商品的价格接口
Gulimall-product 服务中 路径下的 SkuInfoController
Gulimall-cart 服务中的 路径下的远程调用接口 ProductFeignService
第四步、【【微信】】服务中编写远程调用 【【淘密令】】-cart服务 feign接口
【【微信】】服务中 路径下的 CartFeignService接口
- 问题 :Feign远程调用的时候会丢失请求头(因为feign远程调用是新创建了一个request):导致请求在远程调用之后请求头没有携带cookie信息,那么就无法找到那个携带session信息的cookie了,也就无法从redis中获取用户登录信息了(因为每个用户用浏览器登录之后都会存储一个包含他登录信息的cookie,key可以是任意的因为服务器知道它是存储session用的,value是唯一标识这个用户的一个值,服务器识别到这个存储session的cookie之后就会根据它的value去redis中查是否有这个用户的登录信息)。
- 解决:加上feign它的远程调用的请求拦截器。(Re【【微信】】),在这个拦截器中重写apply方法,在这个方法中把老请求的cookie复制到新request的请求头中
- 因为feign在远程调用之前会执行所有的Re【【微信】】拦截器(feign的拦截器)
在 【【微信】】 服务中 路径下编写Feign配置类:GulimallFeignConfig类并编写请求拦截器
此时:查询购物项、库存和收货地址都要调用远程服务,串行执行远程调用会浪费大量时间,因此我们进行异步编排优化
-
问题: 因为我们给feign的拦截器所拦截的请求加上了请求头,但这个获取请求头的过程是在一个线程执行过程中进行的,但是由于 Re【【微信】】底层使用的是线程共享数据 ,我们知道线程共享数据的域是当前线程下,线程之间是不共享的。所以在开启异步时获取不到老请求的信息即拦截器中会发生空指针的错误,即老request对象获取不到,是null,自然也就无法共享了。但是我们现在进行的是异步操作,使用到了线程池,这就导致请求头不在一个线程中,最终导致请求头信息丢失。
-
解决: 向 Re【【微信】】线程域中放主线程的请求域。即先在主线程中获取到请求头相关信息(),然后在异步调用的过程中将情求头加到正在执行异步操作的线程中( ),这样请求头就不会丢失。
虽然都是同一个 Re【【微信】】类在调用方法,但是 Re【【微信】】在不同线程中的意义是不同的,它仅代表当前线程。
修改 【【微信】】 服务中 目录下的 OrderSer【【微信】】 类
修改 【【微信】】 服务中,路径下的 confirm.html
需求:查询订单项有货还是无货
在远程查询购物车所有选中的购物项之后进行批量查询库存
在订单确认页数据获取 Service层实现类 OrderSer【【微信】】 方法中进行批量查询库存
1、修改【【微信】】 服务中 路径下的 OrderSer【【微信】】 类
2、在【【微信】】 服务中创建商品是否有库存的VO类
在 【【微信】】 服务中 路径下创建 SkuStockVo 类
3、【【微信】】 库存服务中提供 查询库存的接口
- 【【微信】】 服务中 路径下的 WareSkuController 类,之前编写过。
- 【【微信】】 服务中编写远程调用 【【微信】】 库存服务中 查询库存 feign接口 【【微信】】 服务下 路径下:WareFeignService
4、页面效果
需求:选择收货地址,计算物流费
选择收货地址页面效果
编写后端接口
【【微信】】仓储服务编写根据用户地址,返回详细地址并计算物流费,修改【【微信】】服务中 路径下 WareInfoController 类
【【微信】】 服务中 路径下 WareInfoSer【【微信】】 类
【【微信】】服务中 路径下 MemberFeignService远程查询地址详细信息feign接口
【【微信】】 服务中 路径下的 Vo
接口幂等性就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的,不会因为多次点击而产生了副作用;比如说支付场景,用户购买了商品支付扣款成功,但是返回结果的时候网络异常,此时钱已经扣了,用户再次点击按钮,此时会进行第二次扣款,返回结果成功,用户查询余额返发现多扣钱了,流水记录也变成了两条,这就没有保证接口的幂等性。我们项目中处理订单的提交时为了避免订单的重复提交,用专业的术语就称之为接口幂等性,通俗点讲就是用户提交一次和用户提交一百次的结果是一样的,数据库中只会有一条订单记录。
简单来说就是无论多少次重复的操作,结果都是一样的。就像数字 1 的无论多少次幂,结果都是 1 。
在我们的项目中如下情况可能需要幂等性处理:
-
用户多次点击按钮
-
用户页面回退再次提交
-
微服务互相调用,由于网络问题,导致请求失败。
-
feign调用其他业务触发重试机制情况
什么情况下需要幂等
以 SQL 为例,有些操作是天然幂等的。
,无论执行多少次都不会改变状态,是天然的幂等。
,无论执行成功多少次状态都是一致的,也是幂等操作。
,多次操作,结果一样,具备幂等性。
如 为唯一主键,即重复操作上面的业务,只 会插入一条用户数据,具备幂等性。
,每次执行的结果都会发生变化,不是幂等的。
如果 不是主键,可以重复,那上面业务多次操 作,数据都会新增多条,不具备幂等性。
(1) Token机制
① 服务端提供了发送 token 的接口。我们在分析业务的时候,哪些业务是存在幂等问题的, 就必须在执行业务前,先去获取 token,服务器会把 token 保存到 redis 中。
② 然后调用业务接口请求时,把 token 携带过去,一般放在请求头部。
③ 服务器判断 token 是否存在 redis 中,存在表示第一次请求,然后删除 token, 继续执行业务。
④ 如果判断 token 不存在 redis 中,就表示是重复操作,直接返回重复标记给 client,这样就保证了业务代码,不被重复执行。
危险性:
① 先删除 token 还是后删除 token
- 先删除可能导致,业务确实没有执行,重试还带上之前 token,由于防重设计导致,请求还是不能执行。
- 后删除可能导致,业务处理成功,但是服务闪断,出现超时,没有删除 token,别人继续重试,导致业务被执行两遍。
- 我们最好设计为先删除 token,如果业务调用失败,就重新获取 token 再次请求。
② Token 获取、比较和删除必须是原子性
、、如果这几个操作不是原子,可能导致高并发下,都 get 到同样的数据,判断都成功,继续业务并发执行。
所以可以在 redis 使用 lua 脚本完成这个操作:
(2) 各种锁机制
1、数据库的悲观锁
悲观锁使用时一般伴随事务一起使用,数据锁定时间可能会很长,需要根据实际情况选用。 另外要注意的是,id 字段一定是主键或者唯一索引,不然可能造成锁表的结果,处理起来会 非常麻烦。
2、数据库的乐观锁
这种方法适合在更新的场景中:
根据 【【微信】】 版本,也就是在操作库存前先获取当前商品的 【【微信】】 版本号,然后操作的时候 带上此 【【微信】】 号。我们梳理下,我们第一次操作库存时,得到【【微信】】 为 1,调用库存服务 【【微信】】 变成了 2;但返回给订单服务出现了问题,订单服务又一次发起调用库存服务,当订单服务传如的 【【微信】】 还是 1,再执行上面的 sql 语句时,就不会执行;因为 【【微信】】 已经变 为 2 了,where 条件就不成立。这样就保证了不管调用几次,只会真正的处理一次。 乐观锁主要使用于处理读多写少的问题。
(3) 业务层分布式锁
如果多个机器可能在同一时间同时处理相同的数据,比如多台机器定时任务都拿到了相同数据处理,我们就可以加分布式锁,锁定此数据,处理完成后释放锁。获取到锁的必须先判断 这个数据是否被处理过。
(4) 各种唯一约束
① 数据库唯一约束
插入数据,应该按照唯一索引进行插入,比如订单号,相同的订单就不可能有两条记录插入。 我们在数据库层面防止重复。
这个机制是利用了数据库的主键唯一约束的特性,解决了在 insert 场景时幂等问题。但主键 的要求不是自增的主键,这样就需要业务生成全局唯一的主键。
如果是分库分表场景下,路由规则要保证相同请求下,落地在同一个数据库和同一表中,要 不然数据库主键约束就不起效果了,因为是不同的数据库和表主键不相关。
② redis set 防重
很多数据需要处理,只能被处理一次,比如我们可以计算数据的 MD5 将其放入 redis 的 set, 每次处理数据,先看这个 MD5 是否已经存在,存在就不处理。
Redis set的防重场景:每个数据的MD5加密后的值唯一,网盘就可以根据上传的数据进行MD5加密,将加密后的数据存储至Redis的set里,下次你上传同样的东西时先会去set进行判断是否存在,存在就不处理。
(5) 防重表
使用订单号 【【微信】】 做为去重表的唯一索引,把唯一索引插入去重表,再进行业务操作,且 他们在同一个事务中。这个保证了重复请求时,因为去重表有唯一约束,导致请求失败,避免了幂等问题。这里要注意的是,去重表和业务表应该在同一库中,这样就保证了在同一个事务,即使业务操作失败了,也会把去重表的数据回滚。这个很好的保证了数据一致性。
之前说的 redis 防重也算。
防重表的应用场景:当我们去解库存的时候,先去防重表里插入一条数据,当请求再次过来的时候,先去防重表里插入数据,只有当插入成功才能进行下一步操作。
(6) 全局请求唯一 id
调用接口时,生成一个唯一 id,redis 将数据保存到集合中(去重),存在即处理过。
可以使用 nginx 设置每一个请求的唯一 id。Nginx为每一个请求设置唯一id可以用作链路追踪,看这个请求请求了那些服务
订单服务的执行流程如下图所示
这里我们先把之前订单提交没有实现的防重令牌实现了,就是向redis缓存中间件中放入一个token,key为一个UUID+订单号,value就是一个令牌(相当于验证码,用来保证订单唯一的),之后再提交订单就会用这个订单的订单号组合令牌前缀去redis中取令牌,只有成功取出令牌才可以处理订单,取不出来表示这个订单不能处理,则直接返回。
【【微信】】服务 路径下的 OrderSer【【微信】】
Controller层编写下单功能接口
【【微信】】 服务 路径下的 OrderWebController 类,代码如下
封装订单提交的VO
- 分析订单页面的提交数据,添加“com.【【微信】】limall.order.vo.【【微信】】”类,代码如下:
说明:京东的结算页中的商品信息是实时获取的,结算的时候会去购物车中再去获取一遍,因此,提交页面的数据Vo没必要提交商品信息
- 前端页面 confirm.html 提供数据
- 问题:存在网路延时,同时提交订单时,从Redis中拿到的令牌相同,导致重复提交
- 原因:因为我们为了实现订单提交的幂等性,所以需要采用一些手段来保证其幂等性,在我们的项目中我们就用了令牌,这个令牌key是前缀+用户id(存疑),va
2千左右燃气热水器推荐哪款 两卫一厨要买多少钱的燃气热水器
2千左右燃气热水器推荐哪款好,两千以内的燃气热水器,2000左右燃气热水器推荐,燃气热水器2000以内的性价比高的以下是几个需要注意的点:
品牌重点考虑国产品牌,重点考虑美的、万和,性价比非常高。
推荐优选搭配水伺服的燃气热水器,最好的控温方式,洗浴体验更佳;
对于零冷水功能,就是打开水龙头便可以留出热水,这点非必须功能,重点看房间布局和个人需求,如果热水器距离浴室距离较远,打开水龙头很长时间才可以留出热水,那么可以重点考虑这个功能。
对于燃气热水器选购技巧,这里不再过多赘述,我的这篇文章做了详细的介绍,如果不是很了解,请移步:
燃气热水器选购全攻略:
2023年,如何选购燃气热水器?林内、能率、卡萨帝、海尔、美的、万和等品牌燃气热水器选购攻略各品牌燃气热水器推荐:能率|林内|海尔|卡萨帝|美的|万和
嗨,大家好,我是小老李,专注家电选购。有兴趣请关注 @小老李聊家电
- 海尔JM6
- 美的MK3-首选
- 万和560J/565w
这几款都是对标产品,性能基本一致,优势就是搭配控温效果最好的水量伺服器,很好的适用水压不稳、全屋存在多点用水的家庭,洗浴体验非常不错,适合预算有限,又追求洗浴体验的顾客。
为了方便对比我列了一张表,方便大家区分差距:
我们详细看一下差异:
- 净重上,美的MK3稍显不错,用量可能不如其他充足;
- 售后方面:美的MK3,采用8年整机质保,售后不用担心,而海尔采用3年整机,关键部位8年;
选购建议:结合价格来看,我最推荐美的MK3,虽然净重稍显不足,但是美的提供8年终生质保,足以可见这款性能是完全有保障,价格也不高,性价比非常高。
选购建议:美的非零冷水中我最推荐的一款,控温效果非常不错,对标海尔JM6,性价比非常高,是支持增压水量伺服器价格最低的一款,采用上置电机+无氧铜换热器+3段7排火排的基础配置,13L、16L容量可供选择,另外还支持自清洁、ECO节能、WiFi智控等功能;整机采用8年质保,非常有保障,适合水压不稳、预算有限,又追求洗浴体验的家庭。
选购建议:核心配置基本一致,冷凝式+零冷水功能,对标海尔ECO、美的LN3,不过这个组合是万和最先推出,价格也最低,性价比不错。
我们都清楚需要将水管中的凉水循环至热水器内重新加热,从而保证打开热水器便可以流出热水,这样势必会增加一部分热气用量;而冷凝式热水器,可以充分利用烟气中热量,从而提高燃气的热效率,这样采用零冷水+冷凝式的组合,实现零冷水的同时,又可以保证燃气用量不增多。
另外这两款净重为14.6kg,用料上非常不错。
缺点就是控温方式采用水气双调,效果一般。
这款适合有零冷水需求,有希望节约燃气,同时水压稳定或者没有多点用水需求的顾客可以考虑。
选购建议:万和WMF7W配置均衡,零冷水+水伺服,11.6kg的净重,用料上也不错,另外还支持专利小魔方,智能控制非常出色。
适合有多点用水或水压不稳、追求极致洗浴体验、大别墅、或者热水器距离浴室距离较远等场景使用,但是预算有相对有限的客户。
燃气热水器选购全攻略:
2023年,如何选购燃气热水器?林内、能率、卡萨帝、海尔、美的、万和等品牌燃气热水器选购攻略各品牌燃气热水器推荐:能率|林内|海尔|卡萨帝|美的|万和
版权声明:除非特别标注原创,其它均来自互联网,转载时请以链接形式注明文章出处。