本篇为项目大纲,详细涉及的知识点可以查看 谷粒商城所学知识点整理总结
基本信息
谷粒商城是一个微服务项目,总体上分为前台购物模块和后台数据管理模块。基于 SpringCloud + SpringCloudAlibaba + MyBatis-Plus + Redis + SpringSession + RabbitMQ + Nginx + ES 实现,Nginx 实现反向代理和动静分离,采用 Docker 容器化部署。前台商城系统包括:用户登录、注册、商品搜索、商品详情、购物车、下订单流程、秒杀活动等模块。后台管理系统包括:系统管理、商品系统、优惠营销、库存系统、订单系统、用户系统、内容管理等七大模块。其中的基本功能都实现,部分细节功能由于时间原因未能实现。项目视频地址。
项目结构与使用组件
注册、配置中心:Nacos
网关:gateway
远程调用:Feign
分布式事务:本地事务+延时队列
限流、降级、熔断:Sentinel
链路追踪:Sleuth + Zipkin 可视化
分别是权限模块(登陆注册)、购物车模块、公共模块(公共的依赖)、优惠活动模块(秒杀活动)、网关、用户模块、订单模块、商品模块、搜索模块、秒杀模块、第三方模块(一些第三方依赖)、仓储模块、后台管理模块(实现后台登陆注册等一些基本功能)。
后台管理
系统管理
主要是一些角色、权限的配置,SQL的监控,定时任务的查看、修改,后台界面菜单的管理等。
商品系统
主要是对商品相关数据的管理。如分类、品牌、属性的查询、修改,商品维护等。
其中属性分为规格属性和销售属性,属性可以被属性组关联。
商品维护分为spu管理、发布商品、商品管理。spu管理主要是对spu的属性进行查看修改,对spu进行上架操作。
上架会将spu的信息保存到ES中,随后就可以在前台搜索栏中检索到。
发布商品则是对商品进行发布操作,配置价格、属性、基本信息等。
图片的保存使用阿里云的OSS存储来实现。
优惠营销
优惠营销主要包括满减折扣、秒杀活动等。秒杀活动可以配置一次秒杀活动,并且配置这次活动中的多个秒杀场次,并且配置每个场次关联的商品。
库存系统
主要是对sku库存消息的管理,一个仓库下可以关联多个商品库存,在库存量不够时可以先创建采购需求,随后将若干个采购需求组成一个采购单,随后再由采购人员拉取采购单,进行采购。
订单系统
主要是查询、修改订单、支付等订单相关的数据。
用户系统
主要是对商城用户的基本信息、会员信息进行查询和修改。
前台功能
注册
前端界面表单使用了JS表单校验,后台使用JSR303进行校验。验证码原本想使用短信验证码,但是发现阿里云、腾讯云都需要项目上线证书验证,所以改用了谷歌的图片验证码配置 Redis 来实现, key 本来准备使用 sessionID 来实现,但是发现获取验证码的请求对应的 sessionId 与提交表单时的 sessionId 是不同的,所以又采用 session.hashCode() 来作为 key。并且在点击时会将发送的验证码请求携带一个时间戳,用于获取新的验证码来实现刷新验证码的功能。
同时密码在存储时使用了 SpringSecurity 的 BCryptPasswordEncoder 来加密,保证密码的安全性。
登陆
使用 OAuth2.0 + SpringSession 实现。
登陆采用了两种登陆方式,用户名密码登陆和第三方社交授权登陆(OAuth2.0),第三方目前只支持微博登陆。
在登陆成功后,会将用户消息通过 SpringSession 保存在 Redis 中,并且向浏览器添加包含用户消息的 Cookie,范围为主域名,以后在不同模块间跳转都可以通过携带的 Cookie 用户信息去 redis 中查到当前用户的信息。
商品检索
使用ES实现。
由于商品检索的条件多,且访问量大,所以使用 ES 来保存检索的商品的主要数据。目前可以通过分类以及搜索栏来检索商品
在检索界面,会根据当前商品相关的销售属性、品牌来显示筛选条件,在增加条件后还会根据该条件下包含的属性类型来动态改变条件。
购物车
使用Redis+本地Cookie实现。
购物车分为未登录状态和登陆状态,在未登陆时,购物车的数据会在 redis 中保存 30 天,在登陆后,购物车数据会一直保存,并且会将购物车数据和之前未登录时加入到购物车的数据进行合并。主要使用 Cookie 来保存购物车数据,在第一次执行后服务器会返回一个保存当前临时用户购物车ID的 Cookie,在登陆后会通过携带的Cookie,查询对应的购物车数据并与登陆用户ID对应的 Redis 数据进行合并,再删除临时Cookie 的数据。同时也实现购物车项的增加删除。
订单系统
1、实现使用了 RabbitMQ 的延时队列+本地事务 来解决分布式事务。同时使用手动确认来保证消息可靠性。
由于下单过程会涉及到很多模块,并且其中数据的准确性也是要求最高的,任意一个数据出错都会对整块数据造成影响。所以应该使用事务对整个过程进行控制,但是因为数据的变动涉及到多个模块,传统的单体事务并不能满足这个场景,所以就引入了分布式事务,但是由于强一致性的分布式事务解决方案会使得系统的性能下降,所以本项目使用的是弱一致性+最终一致性。在调用远程方法锁库存时为了防止发生异常添加了事务,并且发送解库存消息给延时队列,保证在之后主业务发生异常之前锁的库存也可以正常解锁,实现数据最终一致性。
具体实现就是在订单模块的主事务方法中进行订单、订单项数据的添加,并调用远程接口进行锁库存操作。然后根据这次远程调用的结果选择订单的状态,最后发送一个释放库存、修改订单状态的消息给延时队列,并且调用远程的锁库存操作也会发送一个释放库存的消息给延时队列,这样可以解决两个问题。1)一段时间内用户未支付订单库存会自动解锁;2)如果调用的远程锁库操作正常执行,但是本地事务方法发生了异常,那么远程未回滚库存也会在一段时间后也会自动解锁。
2、使用 Redis 存储订单令牌,以此实现支付订单接口的幂等性。
通过 lua脚本来验证和删除令牌(在进入结算页面时会在 redis 存入一个数据作为令牌,随后生成订单时则会通过脚本来检查这个数据是否存在,如果存在就将其删除并返回1,否则返回0,因为需要查询和删除的通过是原子性所以使用脚本来实现),成功后执行业务代码。
3、使用支付宝沙箱环境模拟支付过程。账号:dubfpq8758@sandbox.com。密码和支付密码都是111111。在支付成功后修改订单状态并保存支付流水记录。
4、为了防止在支付界面一直等待直到后台队列将订单取消再完成支付,导致数据错乱,在消息取消订单时同时会关闭支付界面
5、为了防止由于消息积压、网络波动等原因导致解库存的消息先于取消订单的消息被接收执行,导致解库存失败(解库存会先判断订单状态是否是取消状态再进行解库存)所以在取消订单中也会发送一条解库存的消息。并且为了保证解库存的幂等性,在库存表中添加了库存工作单记录用于标记当前订单商品的的库存是否已经解锁了。
秒杀功能
使用定时任务+异步任务来实现秒杀功能。在每天的三点整会触发定时异步任务,将接下来三天内的所有秒杀场次以及秒杀商品的相关信息添加到 Redis 中(防止高并发的请求访问数据库),秒杀方法使用 Sentinel 进行限流控制,秒杀商品会在首页展示,并且在商品详情页中提示。
秒杀商品的下单与普通不同,其会直接跳过加入购物车步骤,直接创建好订单,随后可以直接支付。
秒杀的流程是:
1、校验秒杀验证码(在将秒杀商品展出时会创建一个秒杀随机码保存到商品属性中,并且展示隐藏在秒杀页面里,在秒杀时会取出商品信息进行比较)。
2、校验秒杀时间是否一致。
3、校验单次购买数量是否超过限制。
4、尝试向redis保存 “userID+场次ID+skuId” 为key的数据,如果成功说明是第一次,否则说明当前是重复操作,失败。
5、通过秒杀库存前缀+秒杀商品的秒杀随机码作为key来进行信号量的扣减来模拟锁库操作,再发送消息给秒杀的消息队列,使其在后台对数据库进行锁库操作。
redis 数据结构