项目概述

84 阅读7分钟

在我的项目中,我主要负责订单模块的设计与开发;

订单表的设计

我的项目订单表包括的信息有

订单基础信息:订单号、订单状态、排序字段、是否显示标记等;

价格信息:单价,购买数量,优惠金额,订单总金额;

下单人信息:下单人id,联系方式,位置信息

服务信息:服务类型名称,服务项名称,服务单价,价格单位,购买数量等

订单的状态

订单的状态是该模块中比较重要的一部分,大部分操作都涉及到订单状态的改变;

待支付 : 订单的初始状态。

派单中:用户支付成功后订单的状态由待支付变为派单中。

代服务:服务人员或机构抢单成功订单的状态由派单中变为待服务。

服务中:服务人员开始服务,订单状态变为服务中。

待评价:服务人员完成服务,订单状态变为待评价。

订单完成:用户完成评价,订单状态变为订单完成。

已取消:订单是待支付状态时用户取消订单,订单状态变为已取消。

已关闭:订单已支付状态下取消订单后订单状态变为已关闭。

常见订单号的生成规则而

使用分布式唯一ID生成器(例如Snowflake算法)生成全局唯一的ID作为订单号。Snowflake 算法根据机器ID、时间戳、序号等因素生成,保证全局唯一性,它的优势在于生成的 ID 具有趋势递增、唯一性、高效性等特点. 在我的项目中,使用的订单类型+时间戳+Redis自增序号的组合,

自动取消支付超时订单如何实现

我们项目使用了定时任务加懒加载的方法实现

1、在创建订单时记录订单超时的时间

2、对订单的超时时间、状态字段加索引

3、通过定时任务查询未支付订单且已经超过overtime的记录,取消订单;

4、懒加载的方式是当用户打开订单查看页面时程序去判断是否达到超时时间,如果达到则取消订单;

如何防止订单重复提交

重复提交通常是网络不稳定时,用户提交后没有响应,用户重复点击提交按钮导致

我们的的项目是通过前端和后端共同完成;

前端:在用户点击按钮后,立即将按钮禁用;防止多次点击

后端:我们使用的是分布式锁解决;以用户id+服务id+超时时间 作为分布式锁,锁定下单接口30秒,30秒内只允许提交一次。分布式锁我们是通过redisson来实现,会对锁添加过期时间30秒,并且执行完成后不会主动释放锁,这样在30秒内重复提交订单是,因为锁被占用从而不会重复提交;

在我项目有很多地方用到了aop,首先的spring的事务控制就是基于aop实现,我们在事务方法上添加@transactional注解实现事务控制,spring对service类生成的代理对象,在代理对象中执行事务方法前会开启事务,执行事务方法完成代理对象提交事务,如果执行事务方法中间发生了异常,则回滚事务

另外在项目中使用了spring cache ,在方法上添加缓存注解就可以实现将方法的返回值加入缓存,这个也是基于aop实现,首先代理对象先从缓存中查询是否有数据,如果缓存中有数据则直接返回,没有数据在调用方法请求数据库查询数据并将数据存入缓存

另外我们在使用分布式锁时,自定义了一个aop的切面,针对添加了@lock注解的方法会被代理而对象代理,在代理对象中会先申请分布式锁,执行原始方法,再释放锁;

策略模式

在项目中,取消订单的时候,由于订单的状态非常多,不同订单状态下取消订单的逻辑也不相同,针对这种场景设计了策略模式来进行取消订单的操作

**策略模式:**指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。

策略模式的组成:

抽象策略角色:策略类,通常由一个接口或者抽象类实现。

具体策略角色:包装相关的算法和行为;

环境角色:持有一个策略类的作用,最终给客户端调用。

订单状态机

项目中订单的状态较多

退款功能实现

取消订单执行退款操作

1、首先使用一个事务保存以下数据:

更新订单状态;保存取消订单记录,记录取消的原因等信息;保存退款记录;

2、事务提交后先启动一个线程请求支付服务的退款接口

3、定时任务扫描退款记录表,对未进行退款的记录请求支付服务进行退款,退款成功更新订单的退款状态,并删除退款记录。

说明:

第2步的作用为了第一时间申请退款,因为定时任务会有一定的延迟。

第3步的作用是由定时任务去更新退款的状态,因为调用了退款接口只是申请退款了,退款结果可能还没有拿到,通过定时任务再次请求支付服务的退款接口,拿到退款结果,更新订单的退款状态,并删除退款记录。

索引优化

订单表是电商系统的核心,随着业务的发展,订单表很容易达到百万甚至千万级级别。没有索引,任何查询都是全表扫描,效率极低。在我的项目中,在用户端订单列表查询使用了使用索引优化。因为查询条件中,有两个非必须非null字段,所以设计了两个两个联合索引,将用户id,订单状态,用户端是否展示字段为一组索引,另外一组索引加上排序字段,这样无论这两个参数存在哪一个或者都不存都能找到对应的索引;由于索引的最左前缀法则,sql语句的书写时,顺序一定不能有误,要按照对应的索引书写;

秒杀抢购

在我的项目中,除了常规的订单模块,我还设计并开发了秒杀抢购这一高并发业务模块。该模块旨在应对瞬时大流量冲击,确保系统在高并发下的稳定性、数据一致性以及用户体验。具体包含了以下技术方案:

1. 缓存方案
为了应对秒杀开始时的大量读请求,我们使用了Redis缓存技术来存储热点数据。在秒杀活动开始前,我们会将商品信息、活动信息和最重要的库存数量提前预热到Redis中。这样,大量的商品详情和库存查询请求就直接访问缓存,极大地减轻了数据库的压力,提高了系统的响应速度。

2. 异步处理方案
秒杀的核心思想是“削峰填谷”。当用户点击秒杀后,系统只进行最快速的资格校验(如库存预减、防重复购买等),一旦校验通过,我们会立即返回“抢购中”的提示,同时将抢购成功的信息和订单数据发送到消息队列(如RabbitMQ)中。然后,由后端的多个消费者进程异步、平稳地处理这些队列中的消息,完成实际的订单创建、扣减最终库存等耗时操作。这样就将同步的高并发请求转化为了异步的串行或低并行处理,减轻了系统的实时压力。

3. 防止超卖方案
超卖问题是秒杀系统中的核心挑战。我们的解决方案主要依赖于Redis的原子操作。在用户抢购资格校验阶段,我们使用Redis的decr命令来预减库存。这个操作是原子性的,可以确保在高并发下,每个抢购请求只能成功扣减一个库存,一旦库存扣减到零,后续的所有请求都会因库存不足而失败,从而从根源上防止了超卖。