订单模块
1.工程结构
订单工程结构大致分为
订单基础工程,主要是一些共用的内容如实体类,基础Mapper、状态机、分库分表等
然后是订单管理模块,涉及订单的创建,取消、支付、查询等操作
接着是抢单模块,用户支付完成后,将订单更新到抢单大厅,交给机构或者是服务人员去抢单然后完成服务
还有派单模块,为防止某个订单没有人或机构去抢,运营平台会主动分配订单
最后是历史订单模块,主要是管理历史订单,将一些超过15天的订单数据迁移到历史订单数据库中
2.订单管理模块
1.表设计
因为我们是家政类型的服务项目,考虑到每个服务人员的技能是有限的,所以我们在设计下单功能的时候,一个服务对应一个订单,把订单表设计为单表操作。
表的常用字段
基本信息:订单ID、订单数量、单价、总价、订单状态、支付状态、下单人信息、服务开始时间
服务信息:服务类型ID、服务类型名称、服务项ID、服务项名称
交易信息:支付服务交易单号三方支付号、退款交易单号、三方退款交易单号
3.下单
1.核心业务流程
用户会在小程序端先点击预约,然后选择需要的服务及数量,如果有优惠劵的话还会使用优惠券,最后点击提交订单。
服务端就会先通过OpenFeign远程调用获取到下单用户的信息及服务信息,
然后填充订单数据
再去判断是否使用了优惠券,如果使用了就需要去远程核销优惠券,
最后就完成订单创建,返回订单id
2.优化订单
1首先.我们在远程调用时,为避免服务雪崩,采用了服务保护策略。通过使用@SentinelResource注解的方式,实现了客户端的服务保护,可以使用对应的属性去指定降级和熔断对应的方法,当远程调用不可用时,会直接返回对应的信息,再通过业务判断,抛出异常,从而保护订单服务。
2.订单ID :我们将订单id设计为订单类型+年月日(yyMMdd)+12位通过Redis自增的ID
3.排序字段:我们在订单表设计了一个sort_by字段{服务开始时间+订单号后六位},方便后期在查询时使用
4.为了防止用户重复提交订单,我们采用自定义注解+AOP+分布式锁的方式来实现订单重复提交的功能,我们的锁前缀采用:用户ID:服务项ID:服务开始时间,设置过期时间为10s
5.分布式事务:因为在用户使用优惠券时,需要调用优惠券核销接口,这个时候可能会有分布式事务问题,为了避免这一情况,我使用了seata来保证分布式事务的处理
6.事务方法效率提升:为了提高事务方法的效率,我把创建订单的代码单独抽抽取到一个方法,来避免远程调用带来的性能开销,同时把这个方法抽取到另一个事务类作为单独方法实现调用,避免事务在同一个事务类的事务方法因为调用了另一个事务导致事务失效的问题
4.订单状态管理
1.订单状态类型及流转
我们将订单状态设置为7种
在正常情况下
用户刚下单时,订单状态为待支付状态
当用户支付完成时,订单状态变为派单中
当服务人员或机构接单或抢单成功时,服务人员:待服务,机构:待分配
服务人员上门服务时,订单会显示服务中
服务人员完成服务时,订单状态为已完成
还有一些其他情况如
用户刚下完单因为一些原因取消订单,订单状态会由待支付
或者用户支付完成后因为其他原因,服务人员还没上门又取消订单了,订单状态会由派单中
还有在一些特殊情况下,用户无法取消订单。服务人员或平台可以在相应的情况下取消订单,这时可以直接将订单状态由其他状态转变为已关闭,
状态机
因为订单的状态之间流转过于麻烦,使用了许多条件判断,因此我们使用状态机来解决这个问题,避免硬编码的问题。
状态机是一种设计模式,是用来专门管理多种状态之间的转换问题。包含了四个要素: