目录
- 1 订单退款功能
- 1.1 需求分析
- 1.2 接口分析
- 1.3 退款流程分析
- 1.4 表结构设计
- 1.5 取消未支付订单实现
- 1.5.1 接口开发
- Controller层开发
- Service层开发
- 1.5.2 接口测试
- 1.5 取消已支付订单实现
1 订单退款功能
1.1 需求分析
用户下单成功可以取消订单,在订单的不同状态下去取消订单其执行的逻辑是不同的:
- 待支付状态下取消订单: 更改订单的状态为已取消。
-订单已支付,状态为待派单时取消订单: 更改订单的状态为已关闭,请求支付服务自动退款。
用户在订单列表点击订单信息进入订单详情页面,点击“取消订单”
进入取消订单界面:
选择取消原因,点击“提交”。
1.2 接口分析
前端传入的参数:订单id、取消原因。
响应参数:无,前端根据状态码判断是否成功。
接口名称:取消订单
接口路径:PUT /orders-manager/consumer/orders/cancel
1.3 退款流程分析
根据需求分析,当订单已支付状态为派单中时取消订单后进行自动退款,此时需要调用支付服务的申请退款接口。
流程如下:
取消订单执行如下操作:
1、更新订单状态
待支付状态下取消订单后更新订单状态为“已取消”
派单中状态下取消订单后更新订单状态为“已关闭”
2、保存取消订单记录,记录取消的原因等信息。
3、远程调用支付服务的退款接口申请退款。
取消派单中的订单存在如下问题:
远程调用退款接口操作不放在事务方法中,避免影响数据库性能。
如果远程调用退款接口失败了将无法退款,这个怎么处理?
以上问题采用异步退款的方式来解决:
如下图:
1、使用数据库事务控制,保存以下数据
更新订单状态。
保存取消订单记录表,记录取消的原因等信息。
保存退款记录表。
2、事务提交后先启动一个线程请求支付服务的退款接口(为了及时退款)
3、定时任务扫描退款记录表,对未退款的记录请求支付服务进行退款,退款成功更新订单的退款状态,并删除退款记录。
说明:
第2步的作用为了第一时间申请退款,因为定时任务会有一定的延迟。
第3步的作用是由定时任务去更新退款的状态,因为调用了退款接口只是申请退款了,退款结果可能还没有拿到,通过定时任务再次请求支付服务的退款接口,拿到退款结果。
1.4 表结构设计
订单取消记录表:
create table `jzo2o-orders`.orders_canceled
(
id bigint not null comment '订单id'
constraint `PRIMARY`
primary key,
canceller_id bigint null comment '取消人',
canceler_name varchar(50) null comment '取消人名称',
canceller_type int null comment '取消人类型,1:普通用户,4:运营人员',
cancel_reason varchar(50) null comment '取消原因',
cancel_time datetime null comment '取消时间',
create_time datetime default CURRENT_TIMESTAMP not null comment '创建时间',
update_time datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间'
)
订单退款记录表:存储 待退款的记录
create table `jzo2o-orders`.orders_refund
(
id bigint not null comment '订单id'
constraint `PRIMARY`
primary key,
trading_order_no bigint null comment '支付服务交易单号',
real_pay_amount decimal(10, 2) null comment '实付金额',
create_time datetime default CURRENT_TIMESTAMP null comment '创建时间'
)
1.5 取消未支付订单实现
根据需求,取消订单需要实现两部分功能:
针对未支付订单的取消操作:
- 修改订单的状态为已取消。
- 保存取消订单的记录。
1.5.1 接口开发
Controller层开发
@RestController("operationOrdersController")
@Api(tags = "运营端-订单相关接口")
@RequestMapping("/operation/orders")
public class OperationOrdersController {
@Autowired
private IOrdersManagerService ordersManagerService;
@PutMapping("/cancel")
@ApiOperation("取消订单")
public void cancel(@RequestBody OrderCancelReqDTO orderCancelReqDTO) {
//填充数据
OrderCancelDTO orderCancelDTO = BeanUtil.toBean(orderCancelReqDTO, OrderCancelDTO.class);
CurrentUserInfo currentUserInfo = UserContext.currentUser();
orderCancelDTO.setCurrentUserId(currentUserInfo.getId());
orderCancelDTO.setCurrentUserName(currentUserInfo.getName());
orderCancelDTO.setCurrentUserType(currentUserInfo.getUserType());
ordersManagerService.cancel(orderCancelDTO);
}
}
Service层开发
/**
* 取消订单
* @param orderCancelDTO 取消订单模型
*/
@Override
public void cancel(OrderCancelDTO orderCancelDTO) {
//判断订单是否存在
if(ObjectUtils.isNull(this.getById(orderCancelDTO.getId()))){
throw new CommonException("要取消的订单不存在");
}
Integer ordersStatus = this.getById(orderCancelDTO.getId()).getOrdersStatus();
if(ordersStatus.equals(OrderStatusEnum.NO_PAY.getStatus())){
//对未支付订单进行取消
owner.cancelByNoPay(orderCancelDTO);
}else if(ordersStatus.equals(OrderStatusEnum.DISPATCHING.getStatus())){
//对已支付(派单中)订单进行取消
}else{
throw new CommonException("当前订单状态不支持取消");
}
}
/**
* 未支付状态取消订单
*/
@Transactional(rollbackFor = Exception.class)
public void cancelByNoPay(OrderCancelDTO orderCancelDTO){
//添加订单取消记录
OrdersCanceled ordersCanceled = BeanUtils.toBean(orderCancelDTO,OrdersCanceled.class);
ordersCanceled.setCancellerId(orderCancelDTO.getCurrentUserId());
ordersCanceled.setCancelerName(orderCancelDTO.getCurrentUserName());
ordersCanceled.setCancellerType(orderCancelDTO.getCurrentUserType());
ordersCanceled.setCancelTime(LocalDateTime.now());
canceledService.save(ordersCanceled);
//修改订单状态为已取消
OrderUpdateStatusDTO orderUpdateStatusDTO = new OrderUpdateStatusDTO();
//需要传入 id 原始状态 更新状态
orderUpdateStatusDTO.setId(orderCancelDTO.getId());
orderUpdateStatusDTO.setOriginStatus(OrderStatusEnum.NO_PAY.getStatus());
orderUpdateStatusDTO.setTargetStatus(OrderStatusEnum.CANCELED.getStatus());
Integer res = commonService.updateStatus(orderUpdateStatusDTO);
if(res <= 0){
throw new CommonException("取消订单失败");
}
}
1.5.2 接口测试
先弄个没支付的订单:
点击取消支付:
功能测试完成