订单服务笔记:
file:///E:/%E6%A1%8C%E9%9D%A2/%E8%B0%B7%E7%B2%92%E5%95%86%E5%9F%8E/%E8%B0%B7%E7%B2%92%E5%95%86%E5%9F%8E/%E9%AB%98%E7%BA%A7%E7%AF%87/%E8%AF%BE%E4%BB%B6/09%E3%80%81%E5%95%86%E5%9F%8E%E4%B8%9A%E5%8A%A1.pdf
百度云地址:https://pan.baidu.com/s/1DV6kf6FZWQls5oyvUZBuZA ,密码1111
一、页面环境搭建
流程:购物车–.>去结算–>结算页–>支付页–>二维码
1.静态资源放入nginx、动态资源html放入项目
1).把订单的等待付款的静态资源放入nginx的static/order/detail下
2).把订单的订单页的静态资源放入nginx的static/order/list下
3).把订单的确认页的静态资源放入nginx的static/order/confirm下
4).把订单的支付页的静态资源放入nginx的static/order/pay下
2.配置域名服务—hosts
1).管理员模式打开SwitchHosts.exe
2)…配置订单域名服务
3.修改nginx的配置文件gulimall.conf,已经配置好
4.修改网关
- id: gulimall_order_route
uri: lb://gulimall-order
predicates:
- Host=order.gulimall.com
5.修改html页面的资源路径
static/order/confirm
static/order/detail/
static/order/list
static/order/pay
1). 加入thymeleaf配置
<!--thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
spring.thymeleaf.cache=false
<html lang="en" xmlns:th="http://www.thymeleaf.org">
二、整合SpringSession–分布式session
1.导入session、线程池、redis
1).SpringSesion 的配置
<!--整合spring-session完成Session共享问题-->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
#session保存到redis
spring.session.store-type=redis
#设置session过期时间
server.servlet.session.timeout=30s
启动类:@EnableRedisHttpSession //开启SpringSession
@Configuration
public class SessionConfig {
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setCookieName("GULISESSION");
// serializer.setCookiePath("/");
serializer.setDomainName("gulimall.com");
return serializer;
}
@Bean
public RedisSerializer<Object> springSessionDefaultRedisSerializer(){
return new GenericJackson2JsonRedisSerializer();
}
}
2).线程池 的配置
#线程池的配置
gulimall.thread.core-size=20
gulimall.thread.max-size=200
gulimall.thread.keep-alive-time=10
//线程池配置类
@Configuration
public class MyThreadConfig {
@Bean("executor")
public ThreadPoolExecutor threadPoolExecutor(ThreadPoolConfigProperties pool) {
return new ThreadPoolExecutor(
pool.getCoreSize(),
pool.getMaxSize(),
pool.getKeepAliveTime(),
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(100000),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
}
}
//线程池 参数的配置类
@ConfigurationProperties(prefix = "gulimall.thread")
@Component
@Data
public class ThreadPoolConfigProperties {
private Integer coreSize;
private Integer maxSize;
private Integer keepAliveTime;
}
3).redis的配置
<!--spring整合redis依赖,lettuce容易内存溢出,使用jedis不会-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--导入jedis依赖-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
#配置redis
spring.redis.host=192.168.56.10
spring.redis.port=6379
三、订单介绍
1.订单中心
电商系统涉及到 3 流,分别时信息流,资金流,物流,而订单系统作为中枢将三者有机的集
合起来。
订单模块是电商系统的枢纽,在订单这个环节上需求获取多个模块的数据和信息,同时对这
些信息进行加工处理后流向下个环节,这一系列就构成了订单的信息流通。
1.1.订单构成
1).用户信息
用户信息包括用户账号、用户等级、用户的收货地址、收货人、收货人电话等组成,用户账
户需要绑定手机号码,但是用户绑定的手机号码不一定是收货信息上的电话。用户可以添加
多个收货信息,用户等级信息可以用来和促销系统进行匹配,获取商品折扣,同时用户等级
还可以获取积分的奖励等
2).订单基础信息
订单基础信息是订单流转的核心,其包括订单类型、父/子订单、订单编号、订单状态、订
单流转的时间等。
(1)订单类型包括实体商品订单和虚拟订单商品等,这个根据商城商品和服务类型进行区
分。
(2)同时订单都需要做父子订单处理,之前在初创公司一直只有一个订单,没有做父子订
单处理后期需要进行拆单的时候就比较麻烦,尤其是多商户商场,和不同仓库商品的时候,
父子订单就是为后期做拆单准备的。
(3)订单编号不多说了,需要强调的一点是父子订单都需要有订单编号,需要完善的时候
可以对订单编号的每个字段进行统一定义和诠释。
(4)订单状态记录订单每次流转过程,后面会对订单状态进行单独的说明。
(5)订单流转时间需要记录下单时间,支付时间,发货时间,结束时间/关闭时间等等
3).商品信息
商品信息从商品库中获取商品的 SKU 信息、图片、名称、属性规格、商品单价、商户信息
等,从用户下单行为记录的用户下单数量,商品合计价格等。
4).优惠信息
优惠信息记录用户参与的优惠活动,包括优惠促销活动,比如满减、满赠、秒杀等,用户使
用的优惠券信息,优惠券满足条件的优惠券需要默认展示出来,具体方式已在之前的优惠券
篇章做过详细介绍,另外还虚拟币抵扣信息等进行记录。
为什么把优惠信息单独拿出来而不放在支付信息里面呢?
因为优惠信息只是记录用户使用的条目,而支付信息需要加入数据进行计算,所以做为区分。
5).支付信息
(1)支付流水单号,这个流水单号是在唤起网关支付后支付通道返回给电商业务平台的支
付流水号,财务通过订单号和流水单号与支付通道进行对账使用。
(2)支付方式用户使用的支付方式,比如微信支付、支付宝支付、钱包支付、快捷支付等。
支付方式有时候可能有两个——余额支付+第三方支付。
(3)商品总金额,每个商品加总后的金额;运费,物流产生的费用;优惠总金额,包括促
销活动的优惠金额,优惠券优惠金额,虚拟积分或者虚拟币抵扣的金额,会员折扣的金额等
之和;实付金额,用户实际需要付款的金额。
用户实付金额=商品总金额+运费-优惠总金额
6). 物流信息
物流信息包括配送方式,物流公司,物流单号,物流状态,物流状态可以通过第三方接口来
获取和向用户展示物流每个状态节点。
1.2、订单状态
1). 待付款
用户提交订单后,订单进行预下单,目前主流电商网站都会唤起支付,便于用户快速完成支
付,需要注意的是待付款状态下可以对库存进行锁定,锁定库存需要配置支付超时时间,超
时后将自动取消订单,订单变更关闭状态。
2). 已付款/待发货
用户完成订单支付,订单系统需要记录支付时间,支付流水单号便于对账,订单下放到 WMS
系统,仓库进行调拨,配货,分拣,出库等操作。
3). 待收货/已发货
仓储将商品出库后,订单进入物流环节,订单系统需要同步物流信息,便于用户实时知悉物
品物流状态
4). 已完成
用户确认收货后,订单交易完成。后续支付侧进行结算,如果订单存在问题进入售后状态
5). 已取消
付款之前取消订单。包括超时未付款或用户商户取消订单都会产生这种订单状态。
6). 售后中
用户在付款后申请退款,或商家发货后用户申请退换货。
售后也同样存在各种状态,当发起售后申请后生成售后订单,售后订单状态为待审核,等待
商家审核,商家审核通过后订单状态变更为待退货,等待用户将商品寄回,商家收货后订单
状态更新为待退款状态,退款到用户原账户后订单状态更新为售后成功。
2.订单流程
订单流程是指从订单产生到完成整个流转的过程,从而行程了一套标准流程规则。而不同的
产品类型或业务类型在系统中的流程会千差万别,比如上面提到的线上实物订单和虚拟订单
的流程,线上实物订单与 O2O 订单等,所以需要根据不同的类型进行构建订单流程。
不管类型如何订单都包括正向流程和逆向流程,对应的场景就是购买商品和退换货流程,正
向流程就是一个正常的网购步骤:订单生成–>支付订单–>卖家发货–>确认收货–>交易成功。
而每个步骤的背后,订单是如何在多系统之间交互流转的,可概括如下图
2.1、订单创建与支付
(1) 、订单创建前需要预览订单,选择收货信息等
(2) 、订单创建需要锁定库存,库存有才可创建,否则不能创建
(3) 、订单创建后超时未支付需要解锁库存
(4) 、支付成功后,需要进行拆单,根据商品打包方式,所在仓库,物流等进行拆单
(5) 、支付的每笔流水都需要记录,以待查账
(6) 、订单创建,支付成功等状态都需要给 MQ 发送消息,方便其他系统感知订阅
2.2、逆向流程
(1) 、修改订单,用户没有提交订单,可以对订单一些信息进行修改,比如配送信息,
优惠信息,及其他一些订单可修改范围的内容,此时只需对数据进行变更即可。
(2) 、订单取消,用户主动取消订单和用户超时未支付,两种情况下订单都会取消订
单,而超时情况是系统自动关闭订单,所以在订单支付的响应机制上面要做支付的
限时处理,尤其是在前面说的下单减库存的情形下面,可以保证快速的释放库存。
另外需要需要处理的是促销优惠中使用的优惠券,权益等视平台规则,进行相应补
回给用户。
(3) 、退款,在待发货订单状态下取消订单时,分为缺货退款和用户申请退款。如果是
全部退款则订单更新为关闭状态,若只是做部分退款则订单仍需进行进行,同时生
成一条退款的售后订单,走退款流程。退款金额需原路返回用户的账户。
(4) 、发货后的退款,发生在仓储货物配送,在配送过程中商品遗失,用户拒收,用户
收货后对商品不满意,这样情况下用户发起退款的售后诉求后,需要商户进行退款
的审核,双方达成一致后,系统更新退款状态,对订单进行退款操作,金额原路返
回用户的账户,同时关闭原订单数据。仅退款情况下暂不考虑仓库系统变化。如果
发生双方协调不一致情况下,可以申请平台客服介入。在退款订单商户不处理的情
况下,系统需要做限期判断,比如 5 天商户不处理,退款单自动变更同意退款。
3、幂等性处理
参照幂等性文档
4、订单业务
4.1、搭建环境
订单服务引入页面,nginx 配置动静分离,上传静态资源到 nginx。编写 controller 跳转逻辑
4.2、订单确认页
可以发现订单结算页,包含以下信息:
- 收货人信息:有更多地址,即有多个收货地址,其中有一个默认收货地址
- 支付方式:货到付款、在线支付,不需要后台提供
- 送货清单:配送方式(不做)及商品列表(根据购物车选中的 skuId 到数据库中查询)
- 发票:不做
- 优惠:查询用户领取的优惠券(不做)及可用积分(京豆)
OrderConfirmVo
@Data
public class OrderConfirmVO {
// 收货地址,ums_member_receive_address 表
private List addresses;
// 购物清单,根据购物车页面传递过来的 skuIds 查询
private List orderItems;
// 可用积分,ums_member 表中的 integration 字段
private Integer bounds;
// 订单令牌,防止重复提交
private String orderToken;
}
OrderItemVO(参照 Cart 对象)
public class OrderItemVo {
private Long skuId;
private Boolean check = true;
private String title;
private String image;
private List skuAttr;
private BigDecimal price;
private Integer count;
private BigDecimal totalPrice;
4.3、创建订单
当用户点击提交订单按钮,应该收集页面数据提交到后台并生成订单数据。
1)、数据模型
订单确认页,需要提交的数据:
@Data
public class OrderSubmitVO {
//提交上次订单确认页给你的令牌;
private String orderToken;
private BigDecimal apyPrice; // 校验总价格时,拿计算价格和这个价格比较
private Integer payType;//0-在线支付 1-货到付款
private String delivery_company; // 配送方式
// 订单清单可以不用提交,继续从购物车中获取
// 地址信息,提交地址 id,会员 id 不需要提交
Private Long addrId;
// TODO:发票相关信息略
// TODO:营销信息等
}
提交以后,需要响应的数据:
@Data
public class OrderSubmitResponseVO {
private OrderEntity orderEntity;
private Integer code;
// 1-不可重复提交或页面已过期 2-库存不足 3-价格校验不合法 等
}
2)、防止超卖
数据库 unsigned int 做最后的保证。
4.4、自动关单
订单超时未支付,需要取消订单
4.5、解锁库存
订单关闭,需要解锁已经占用的库存
库存锁定成功,订单回滚,保证最终一致性,也需要库存自动解锁
功能参照消息队列流程完成
5.秒杀
5.1、秒杀业务
秒杀具有瞬间高并发的特点,针对这一特点,必须要做限流 + 异步 + 缓存(页面静态化)
- 独立部署。
限流方式:
- 前端限流,一些高并发的网站直接在前端页面开始限流,例如:小米的验证码设计
- nginx 限流,直接负载部分请求到错误的静态页面:令牌算法 漏斗算法
- 网关限流,限流的过滤器
- 代码中使用分布式信号量
- rabbitmq 限流(能者多劳:chanel.basicQos(1)),保证发挥所有服务器的性能。
5.2、秒杀流程
见秒杀流程图
5.3、限流
参照 Alibaba Sentinel