学习目标
1、理解订单加锁的意义,能够复述订单可核算订单项的操作流程【总述、添加菜品、移除菜品】
2、掌握转台的前提条件并且完成转台功能开发
3、掌握为什么要根据订单生成交易单,交易单类型有那些
4、掌握发起退款的前提条件并且完成发起退款功能开发
第一章 商家平台-订单操作
1、功能区拆解

用户在下单后,对菜品数量进行调整,可告知服务员,服务员可以在商家后台进行订单操作,做订单操作需要满足下列条件
1、当前桌台的订单状态:待付款【DFK】
2、追加菜品,菜品状态为起售,且库存足够
3、退菜时,当菜品数量变0,但不会删除订单项,且退菜不可以把库存退成负数
下图为整个订单项操作的流程图,我们首先进行下流程分析:

2、功能开发

- OrderController
传入菜品、订单号、操作类型
@PostMapping("opertion-to-orderItem/{dishId}/{orderNo}/{opertionType}")@ApiOperation(value = "操作订单菜品数量",notes = "操作订单菜品数量")@ApiImplicitParams({@ApiImplicitParam(paramType = "path",name = "dishId",value = "菜品ID",dataType = "Long"),@ApiImplicitParam(paramType = "path",name = "orderNo",value = "订单编号",dataType = "Long"),@ApiImplicitParam(paramType = "path",name = "opertionType",value = "操作动作",example = "ADD",dataType = "String")})public ResponseWrap<OrderVo> opertionToOrderItem(@PathVariable("dishId") Long dishId,@PathVariable("orderNo") Long orderNo,@PathVariable("opertionType") String opertionType) throws ProjectException {OrderVo orderVo = orderFace.opertionToOrderItem(dishId,orderNo,opertionType);return ResponseWrapBuild.build(BrandEnum.SUCCEED,orderVo);}
- OrderFace
/**** @description 调整订单项菜品数量* @param dishId 菜品ID* @param orderNo 订单* @param opertionType 操作类型* @return*/OrderVo opertionToOrderItem(Long dishId, Long orderNo, String opertionType) throws ProjectException;
- OrderFaceImpl

1、判定订单待支付状态才可操作
2、判定菜品处于起售状态才可操作
3、锁定订单
4、操作订单项
4.1、添加可核算订单项4.2、移除可核算订单项5、计算订单总金额
6、修改订单总金额
7、返回新订单信息
@Override@GlobalTransactionalpublic OrderVo opertionToOrderItem(Long dishId,Long orderNo,String opertionType)throws ProjectException {//1、判定订单待支付状态才可操作OrderVo orderVoResult = orderService.findOrderByOrderNo(orderNo);if (!SuperConstant.DFK.equals(orderVoResult.getOrderState())){throw new ProjectException(OrderEnum.STATUS_FAIL);}//2、判定菜品处于起售状态才可操作Dish dish = dishService.getById(dishId);if (!SuperConstant.YES.equals(dish.getEnableFlag())||!SuperConstant.YES.equals(dish.getDishStatus())){throw new ProjectException(OrderEnum.DISH_STATUS_FAIL);}//3、锁定订单String keyOrderItem = AppletCacheConstant.ADD_TO_ORDERITEM_LOCK+orderNo;RLock lockOrderItem = redissonClient.getLock(keyOrderItem);try {if (lockOrderItem.tryLock(AppletCacheConstant.REDIS_WAIT_TIME,AppletCacheConstant.REDIS_LEASETIME,TimeUnit.SECONDS)){String key = AppletCacheConstant.REPERTORY_DISH+dishId;RAtomicLong atomicLong = redissonClient.getAtomicLong(key);//4.1添加可核算订单项if (opertionType.equals(SuperConstant.OPERTION_TYPE_ADD)){this.addToOrderItem(dishId,orderNo,atomicLong);}//4.2、移除可核算订单项if (opertionType.equals(SuperConstant.OPERTION_TYPE_REMOVE)){this.removeToOrderItem(dishId,orderNo,atomicLong);}//5、计算订单总金额List<OrderItem> orderItemListResult = orderItemService.findOrderItemByOrderNo(orderNo);BigDecimal sumPrice = orderItemListResult.stream().map(n->{BigDecimal price = n.getPrice();BigDecimal reducePrice = n.getReducePrice();Long dishNum = n.getDishNum();//如果有优惠价格以优惠价格计算if (EmptyUtil.isNullOrEmpty(reducePrice)){return price.multiply(new BigDecimal(dishNum));}else {return reducePrice.multiply(new BigDecimal(dishNum));}}).reduce(BigDecimal.ZERO, BigDecimal::add);orderVoResult.setPayableAmountSum(sumPrice);//6、修改订单总金额OrderVo orderVoHandler = OrderVo.builder().id(orderVoResult.getId()).payableAmountSum(sumPrice).build();boolean flag = orderService.updateById(BeanConv.toBean(orderVoHandler, Order.class));if (!flag){throw new ProjectException(OrderItemEnum.SAVE_ORDER_FAIL);}//7、返回新订单信息if (!EmptyUtil.isNullOrEmpty(orderVoResult)){List<OrderItemVo> orderItemVoStatisticsList = BeanConv.toBeanList(orderItemListResult, OrderItemVo.class);orderVoResult.setOrderItemVoStatisticsList(orderItemVoStatisticsList);return orderVoResult;}}return orderVoResult;} catch (InterruptedException e) {log.error("操作订单信息异常:{}", ExceptionsUtil.getStackTraceAsString(e));throw new ProjectException(OrderEnum.OPERTION_SHOPPING_CART_FAIL);}finally {lockOrderItem.unlock();}}

可核算订单项-添加
1、如果库存够,redis减库存
2、修改可核算订单项
3、减菜品库存
4、减菜品库存失败,归还redis菜品库存
5、redis库存不足,虽然可以不处理,但建议还是做归还库存
/*** @description 添加可核算订单项* @param dishId 菜品ID* @param orderNo 订单编号* @param atomicLong 原子计数器* @return*/private void addToOrderItem(Long dishId, Long orderNo, RAtomicLong atomicLong) throws ProjectException {//1、如果库存够,redis减库存if (atomicLong.decrementAndGet()>=0){//2、修改可核算订单项Boolean flagOrderItem = orderItemService.updateDishNum(1L, dishId,orderNo);//3、减菜品库存Boolean flagDish = dishService.updateDishNumber(-1L,dishId);if (!flagOrderItem||!flagDish){//4、减菜品库存失败,归还redis菜品库存atomicLong.incrementAndGet();throw new ProjectException(ShoppingCartEnum.UPDATE_DISHNUMBER_FAIL);}}else {//5、redis库存不足,虽然可以不处理,但建议还是做归还库存atomicLong.incrementAndGet();throw new ProjectException(ShoppingCartEnum.UNDERSTOCK);}}
可核算订单项-移除

1、修改订单项
2、菜品库存
3、添加缓存中的库存数量
/*** @description 移除订单项* @param dishId 菜品ID* @param orderNo 订单编号* @param atomicLong 原子计数器* @return*/private void removeToOrderItem(Long dishId, Long orderNo, RAtomicLong atomicLong) throws ProjectException {//1、修改订单项Boolean flagOrderItem = orderItemService.updateDishNum(-1L, dishId,orderNo);//2、菜品库存Boolean flagDish = dishService.updateDishNumber(1L,dishId);if (!flagOrderItem||!flagDish){throw new ProjectException(ShoppingCartEnum.UPDATE_DISHNUMBER_FAIL);}//3、添加缓存中的库存数量atomicLong.incrementAndGet();}
第二章 商家平台-转台操作
1、功能区拆解
转台功能:用户在就餐过程中,因当前桌台不符合其要求,则可寻求服务人员帮其进行换台,需要满足下列条件
1、桌台状态:当前桌台【使用中】、目标桌台【空闲中】
2、目标桌台,不能是当前桌台
3、当前桌台的账单状态:待付款【DFK】
点击【转台】:

选择【桌台】

点击【确定】

2、功能开发

- AppletController:
@PostMapping("rotary-table/{sourceTableId}/{targetTableId}/{orderNo}")@ApiOperation(value = "转台",notes = "转台")@ApiImplicitParams({@ApiImplicitParam(paramType = "path",name = "sourceTableId",value = "源桌台",dataType = "Long"),@ApiImplicitParam(paramType = "path",name = "targetTableId",value = "目标桌台",dataType = "Long"),@ApiImplicitParam(paramType = "path",name = "orderNo",value = "订单编号",dataType = "Long")})public ResponseWrap<Boolean> rotaryTable(@PathVariable("sourceTableId") Long sourceTableId,@PathVariable("targetTableId") Long targetTableId,@PathVariable("orderNo") Long orderNo) {Boolean flag = appletFace.rotaryTable(sourceTableId,targetTableId,orderNo);return ResponseWrapBuild.build(BrandEnum.SUCCEED,flag);}
- AppletFace
/**** @description 转台业务,满足下列条件才可以转台:* 1、oldTableId处于USE状态* 2、newTableId处于FREE状态* 3、orderNo处于未付款,支付中状态* @param sourceTableId 源桌台* @param targetTableId 目标桌台* @param orderNo 订单号* @return*/Boolean rotaryTable(Long sourceTableId,Long targetTableId,Long orderNo) throws ProjectException;
- AppletFaceImpl

1、锁定目标桌台
2、查询目标桌台
2.1、桌台空闲2.2、桌台非空闲3、订单关联新桌台
4、修改桌台状态
@Override@Transactionalpublic Boolean rotaryTable(Long sourceTableId, Long targetTableId, Long orderNo) throws ProjectException {//1、锁定目标桌台String keyTargetTableId = AppletCacheConstant.OPEN_TABLE_LOCK + targetTableId;RLock lock = redissonClient.getLock(keyTargetTableId);try {Boolean flag = true;if (lock.tryLock(AppletCacheConstant.REDIS_WAIT_TIME,AppletCacheConstant.REDIS_LEASETIME,TimeUnit.SECONDS)) {//2、查询目标桌台Table targetTable = tableService.getById(targetTableId);//2.1、桌台空闲if (SuperConstant.FREE.equals(targetTable.getTableStatus())) {//3、订单关联新桌台flag = orderService.rotaryTable(sourceTableId, targetTableId, orderNo);if (flag) {//4、修改桌台状态tableService.updateTable(TableVo.builder().id(targetTableId).tableStatus(SuperConstant.USE).build());tableService.updateTable(TableVo.builder().id(sourceTableId).tableStatus(SuperConstant.FREE).build());} else {throw new ProjectException(RotaryTableEnum.ROTARY_TABLE_FAIL);}//2.2桌台非空闲} else {throw new ProjectException(RotaryTableEnum.ROTARY_TABLE_FAIL);}}return flag;}catch (Exception e){log.error("操作购物车详情异常:{}", ExceptionsUtil.getStackTraceAsString(e));throw new ProjectException(TableEnum.ROTARY_TABLE_FAIL);}finally {lock.unlock();}}
- IOrderService
/**** @description 转台操作* @param sourceTableId 源桌台* @param targetTableId 目标桌台* @param orderNo* @return*/Boolean rotaryTable(Long sourceTableId, Long targetTableId, Long orderNo);
- IOrderServiceImpl
@Overridepublic Boolean rotaryTable(Long sourceTableId, Long targetTableId, Long orderNo) {//查询目标桌台Table table = tableService.getById(targetTableId);//订单修改Order order = Order.builder().tableId(table.getId()).tableName(table.getTableName()).areaId(table.getAreaId()).build();LambdaQueryWrapper<Order> lambdaQueryWrapper = new LambdaQueryWrapper<>();lambdaQueryWrapper.eq(Order::getTableId,sourceTableId).eq(Order::getOrderNo,orderNo);lambdaQueryWrapper.eq(Order::getOrderState,SuperConstant.DFK);return update(order,lambdaQueryWrapper);}
第三章 商家平台-发起结算、发起退款
1、发起订单结算
用户就餐完成之后,去前台进行结算,服务人员进入后天进行结算操作
1.1、功能区拆解


订单状态:
订单的状态都被数字字典维护,直接从数字字典中读取
| 状态 | 解释 |
|---|---|
| 待支付 | 用户下单创建订单时 |
| 支付中 | 收银员发起结算,生产二维码,用户未扫码或扫码后,三方未给出最终结果前 |
| 支付失败 | 三方告知当前订单结算失败时 |
| 已结算 | 三方告知当前订单结算成功时 |
| 免单 | 当前订单无需支付,直接结单 |
| 退款【单独字段】 | 已结算后发起的线下 退款操作 |


| 名词 | 解释 |
|---|---|
| 优惠 | 当前结算人员对当前订单进行优惠操作==【与员工权限有关】== |
| 折扣 | 当前结算人员对当前订单进行折扣操作==【与员工权限有关】== |
| 结算方式【结算渠道】 | 支付宝、现金、免单….. |
下图为整个订单结算的流程图,我们首先进行下流程分析:

1.2、功能开发

- OrderController
传入要结算的订单信息发起订单结算,获得结算人信息,这里我们可以从UserVoContext中直接获得当前 登录的员工==【统一权限中介绍】==
@PostMapping("handleTrading")@ApiOperation(value = "发起订单结算",notes = "发起订单结算")@ApiImplicitParam(name = "orderVo",value = "订单信息",dataType = "OrderVo")public ResponseWrap<TradingVo> handleTrading(@RequestBody OrderVo orderVo){//获得结算人信息String userVoString = UserVoContext.getUserVoString();UserVo userVo = JSONObject.parseObject(userVoString, UserVo.class);orderVo.setCashierId(userVo.getId());orderVo.setCashierName(userVo.getUsername());TradingVo tradingVo = orderFace.handleTrading(orderVo);return ResponseWrapBuild.build(BrandEnum.SUCCEED,tradingVo);}@PostMapping("handle-trading-md")@ApiOperation(value = "订单免单",notes = "订单免单")@ApiImplicitParam(name = "orderVo",value = "订单信息",dataType = "OrderVo")public ResponseWrap<Boolean> handleTradingMd(@RequestBody OrderVo orderVo){//获得当前订单结算人信息String userVoString = UserVoContext.getUserVoString();UserVo userVo = JSONObject.parseObject(userVoString, UserVo.class);orderVo.setCashierId(userVo.getId());orderVo.setCashierName(userVo.getUsername());Boolean flag = orderFace.handleTradingMd(orderVo);return ResponseWrapBuild.build(BrandEnum.SUCCEED,flag);}@PostMapping("handle-trading-gz")@ApiOperation(value = "订单挂账",notes = "订单挂账")@ApiImplicitParam(name = "orderVo",value = "订单信息",dataType = "OrderVo")public ResponseWrap<Boolean> handleTradingGz(@RequestBody OrderVo orderVo){//获得当前订单结算人信息String userVoString = UserVoContext.getUserVoString();UserVo userVo = JSONObject.parseObject(userVoString, UserVo.class);orderVo.setCashierId(userVo.getId());orderVo.setCashierName(userVo.getUsername());Boolean flag = orderFace.handleTradingGz(orderVo);return ResponseWrapBuild.build(BrandEnum.SUCCEED,flag);}
- OrderFace
/**** @description 发起订单结算* @param orderVo 订单信息* @return: com.itheima.restkeeper.req.TradingVo*/TradingVo handleTrading(OrderVo orderVo)throws ProjectException;
- OrderFaceImpl
细节因为要调用多个服务这里需要添加@GlobalTransactional【seata-at中讲解】
1、根据订单生成交易单】
2、调用支付RPC接口,进行支付
3、结算后桌台状态修改:开桌—>空闲
4、修改桌台状态
@Override@GlobalTransactionalpublic TradingVo handleTrading(OrderVo orderVo) throws ProjectException{//1、根据订单生成交易单TradingVo tradingVo = tradingConvertor(orderVo);if (EmptyUtil.isNullOrEmpty(tradingVo)){throw new ProjectException(OrderEnum.FAIL);}//2、调用统一收单线下交易预创建TradingVo tradingVoResult = null;if (TradingConstant.TRADING_CHANNEL_WECHAT_PAY.equals(orderVo.getTradingChannel())||TradingConstant.TRADING_CHANNEL_ALI_PAY.equals(orderVo.getTradingChannel())){tradingVoResult = nativePayFace.createDownLineTrading(tradingVo);}else if (TradingConstant.TRADING_CHANNEL_CASH_PAY.equals(orderVo.getTradingChannel())){tradingVoResult = cashPayFace.createCachTrading(tradingVo);}else{throw new ProjectException(OrderEnum.PLACE_ORDER_FAIL);}//3、结算后桌台状态修改:开桌-->空闲Boolean flag = true;if (EmptyUtil.isNullOrEmpty(tradingVoResult)){throw new ProjectException(OrderEnum.FAIL);}else {TableVo tableVo = TableVo.builder().id(orderVo.getTableId()).tableStatus(SuperConstant.FREE).build();//4、修改桌台状态flag = tableFace.updateTable(tableVo);if (!flag){throw new ProjectException(OrderEnum.FAIL);}}return tradingVoResult;}
订单生成交易单转换
根据不同的请求动作【付款、退款、免单、挂账】,我们进行不同的交易单的构建,这里之所以要把订单转换为交易单,只要是为了==【解耦系统】==订单与业务相关,交易是一个独立的系统
/**** @description 转换订单为交易单* @param orderVo 订单信息* @return: com.itheima.restkeeper.req.TradingVo*/private TradingVo tradingConvertor(OrderVo orderVo)throws ProjectException {//付款动作if (orderVo.getTradingType().equals(TradingConstant.TRADING_TYPE_FK)){return payTradingVo(orderVo);//退款动作}else if (orderVo.getTradingType().equals(TradingConstant.TRADING_TYPE_TK)){return refundTradingVo(orderVo);//免单动作}else if (orderVo.getTradingType().equals(TradingConstant.TRADING_TYPE_MD)) {return freeChargeTradingVo(orderVo);//挂账动作}else if (orderVo.getTradingType().equals(TradingConstant.TRADING_TYPE_GZ)) {return creditTradingVo(orderVo);}else {throw new ProjectException(TradingEnum.TRADING_TYPE_FAIL);}}
付款动作,交易单生成
/**** @description 付款动作* @param orderVo* @return TradingVo 交易单*/private TradingVo payTradingVo(OrderVo orderVo){Order order = orderService.getById(orderVo.getId());//应付总金额BigDecimal payableAmountSum = order.getPayableAmountSum();//打折BigDecimal discount = orderVo.getDiscount();//优惠BigDecimal reduce = orderVo.getReduce();//计算实付总金额BigDecimal realAmountSum = payableAmountSum.multiply(discount.divide(new BigDecimal(10))).subtract(reduce);//实付金额order.setRealAmountSum(realAmountSum);//收银人idorder.setCashierId(orderVo.getCashierId());//收银人名称order.setCashierName(orderVo.getCashierName());//支付渠道order.setTradingChannel(orderVo.getTradingChannel());//支付类型order.setTradingType(orderVo.getTradingType());//订单状态:现金结算:YJS,支付宝,微信结算:FKZif (TradingConstant.TRADING_CHANNEL_CASH_PAY.equals(orderVo.getTradingChannel())){order.setOrderState(TradingConstant.YJS);}else {order.setOrderState(TradingConstant.FKZ);}//防止修改分库分表分片键order.setShardingId(null);order.setOrderNo(null);boolean flag = orderService.updateById(order);//构建交易单if (flag){TradingVo tradingVo = TradingVo.builder().tradingAmount(realAmountSum).tradingChannel(orderVo.getTradingChannel()).tradingType(orderVo.getTradingType()).tradingState(order.getOrderState()).enterpriseId(order.getEnterpriseId()).isRefund(order.getIsRefund()).storeId(order.getTableId()).payeeId(orderVo.getCashierId()).payeeName(orderVo.getCashierName()).productOrderNo(orderVo.getOrderNo()).memo(order.getTableName()+":"+order.getOrderNo()).build();return tradingVo;}else {throw new ProjectException(OrderEnum.UPDATE_FAIL);}}
退款动作,交易单生成
/**** @description 退款动作* @param orderVo* @return TradingVo 交易单*/private TradingVo refundTradingVo(OrderVo orderVo){Order order = orderService.getById(orderVo.getId());//修改退款金额order.setRefund(order.getRefund().add(orderVo.getOperTionRefund()));//有退款行为order.setIsRefund(SuperConstant.YES);//防止修改分库分表分片键order.setShardingId(null);order.setOrderNo(null);boolean flag = orderService.updateById(order);//构建交易单if (flag){TradingVo tradingVo = TradingVo.builder().operTionRefund(orderVo.getOperTionRefund())//前台传递过来退款金额.tradingAmount(order.getRealAmountSum())//订单实付金额.refund(order.getRefund())//订单退款金额.isRefund(SuperConstant.YES).tradingChannel(orderVo.getTradingChannel()).tradingType(orderVo.getTradingType()).enterpriseId(orderVo.getEnterpriseId()).productOrderNo(orderVo.getOrderNo()).memo(orderVo.getTableName()+":"+orderVo.getOrderNo()).build();return tradingVo;}else {throw new ProjectException(OrderEnum.UPDATE_FAIL);}}
免单动作,交易单生成
/**** @description 免单动作* @param orderVo* @return TradingVo 交易单*/private TradingVo freeChargeTradingVo(OrderVo orderVo){Order order = orderService.getById(orderVo.getId());CustomerVo customerVo = customerFace.findCustomerByCustomerId(orderVo.getBuyerId());//支付渠道order.setTradingChannel(orderVo.getTradingChannel());//支付类型order.setTradingType(orderVo.getTradingType());//订单状态:MDorder.setOrderState(TradingConstant.MD);//收银人idorder.setCashierId(orderVo.getCashierId());//收银人名称order.setCashierName(orderVo.getCashierName());//防止修改分库分表分片键order.setShardingId(null);order.setOrderNo(null);//结算保存订单信息boolean flag = orderService.updateById(order);//构建交易单if (flag){TradingVo tradingVo = TradingVo.builder().tradingAmount(order.getPayableAmountSum()).tradingChannel(orderVo.getTradingChannel()).tradingType(orderVo.getTradingType()).tradingState(order.getOrderState()).enterpriseId(orderVo.getEnterpriseId()).storeId(orderVo.getTableId()).payerId(customerVo.getId()).payerName(customerVo.getMobil()).payeeId(orderVo.getCashierId()).payeeName(orderVo.getCashierName()).productOrderNo(orderVo.getOrderNo()).memo(orderVo.getTableName()+":"+orderVo.getOrderNo()).build();return tradingVo;}else {throw new ProjectException(OrderEnum.UPDATE_FAIL);}}
挂账动作,交易单生成
/**** @description 挂账动作* @param orderVo* @return TradingVo 交易单*/private TradingVo creditTradingVo(OrderVo orderVo){Order order = orderService.getById(orderVo.getId());CustomerVo customerVo = customerFace.findCustomerByCustomerId(orderVo.getBuyerId());//支付渠道order.setTradingChannel(orderVo.getTradingChannel());//支付类型order.setTradingType(orderVo.getTradingType());//订单状态:MDorder.setOrderState(TradingConstant.GZ);//收银人idorder.setCashierId(orderVo.getCashierId());//收银人名称order.setCashierName(orderVo.getCashierName());//防止修改分库分表分片键order.setShardingId(null);order.setOrderNo(null);//结算保存订单信息boolean flag = orderService.updateById(order);//构建交易单if (flag){TradingVo tradingVo = TradingVo.builder().tradingAmount(order.getPayableAmountSum()).tradingChannel(orderVo.getTradingChannel()).tradingType(orderVo.getTradingType()).tradingState(order.getOrderState()).enterpriseId(orderVo.getEnterpriseId()).storeId(orderVo.getTableId()).payerId(customerVo.getId()).payerName(customerVo.getMobil()).payeeId(orderVo.getCashierId()).payeeName(orderVo.getCashierName()).productOrderNo(orderVo.getOrderNo()).memo(orderVo.getTableName()+":"+orderVo.getOrderNo()).build();return tradingVo;}else {throw new ProjectException(OrderEnum.UPDATE_FAIL);}}
2、发起订单退款
2.1、功能区拆解
用户订单支付完成后,发现支付金额不正确,需要收银员进行退款操作,退款的前提条件:
1、订单处于【已结算】,才可退款
2、退款金额不能超过实付款,退款的渠道只能原路返回
3、收款人与退款人必须是同一个人,如果不同不可退款


下图为整个订单退款的流程图,我们首先进行下流程分析:

2.1、功能开发

- OrderController
传入要结算的订单信息发起订单结算,获得当前订单结算人信息
@PostMapping("handle-trading-refund")@ApiOperation(value = "订单退款",notes = "订单退款")@ApiImplicitParam(name = "orderVo",value = "订单信息",dataType = "OrderVo")public ResponseWrap<Boolean> handleTradingRefund(@RequestBody OrderVo orderVo){//获得当前订单结算人信息String userVoString = UserVoContext.getUserVoString();UserVo userVo = JSONObject.parseObject(userVoString, UserVo.class);orderVo.setCashierId(userVo.getId());orderVo.setCashierName(userVo.getUsername());Boolean flag = orderFace.handleTradingRefund(orderVo);return ResponseWrapBuild.build(BrandEnum.SUCCEED,flag);}
- OrderFace
/**** @description 发起订单退款* @param orderVo 订单信息* @return: com.itheima.restkeeper.req.TradingVo*/Boolean handleTradingRefund(OrderVo orderVo)throws ProjectException;
- OrderFaceImpl
细节因为要调用多个服务这里需要添加@GlobalTransactional【seata-at中讲解】
1、获取当前交易单信息
2、退款收款人不为同一人,退款操作拒绝
3、当前交易单信息,退款操作拒绝
4、根据订单生成交易单
5、执行退款交易
@Override@GlobalTransactionalpublic Boolean handleTradingRefund(OrderVo orderVo)throws ProjectException {//1、获取当前交易单信息OrderVo orderVoBefore = findOrderVoPaid(orderVo.getOrderNo());//2、退款收款人不为同一人,退款操作拒绝if (orderVo.getCashierId().longValue()!=orderVoBefore.getCashierId().longValue()){throw new ProjectException(OrderEnum.REFUND_FAIL);}//3、当前交易单信息,退款操作拒绝if (!TradingConstant.YJS.equals(orderVoBefore.getOrderState())){throw new ProjectException(OrderEnum.REFUND_FAIL);}//4、根据订单生成交易单TradingVo tradingVo = tradingConvertor(orderVo);if (EmptyUtil.isNullOrEmpty(tradingVo)){throw new ProjectException(OrderEnum.FAIL);}//5、执行统一收单交易退款接口TradingVo tradingVoResult = nativePayFace.refundDownLineTrading(tradingVo);if (EmptyUtil.isNullOrEmpty(tradingVoResult)){throw new ProjectException(OrderEnum.FAIL);}return true;}
订单生成交易单转换
根据不同的请求动作【付款、退款、免单、挂账】,我们进行不同的交易单的构建,这里之所以要把订单转换为交易单,只要是为了==【解耦系统】==订单与业务相关,交易是一个独立的系统
/**** @description 转换订单为交易单* @param orderVo 订单信息* @return: com.itheima.restkeeper.req.TradingVo*/private TradingVo tradingConvertor(OrderVo orderVo)throws ProjectException {//付款动作if (orderVo.getTradingType().equals(TradingConstant.TRADING_TYPE_FK)){return payTradingVo(orderVo);//退款动作}else if (orderVo.getTradingType().equals(TradingConstant.TRADING_TYPE_TK)){return refundTradingVo(orderVo);//免单动作}else if (orderVo.getTradingType().equals(TradingConstant.TRADING_TYPE_MD)) {return freeChargeTradingVo(orderVo);//挂账动作}else if (orderVo.getTradingType().equals(TradingConstant.TRADING_TYPE_GZ)) {return creditTradingVo(orderVo);}else {throw new ProjectException(TradingEnum.TRADING_TYPE_FAIL);}}
退款渠道,交易单生成
/**** @description 退款动作* @param orderVo* @return TradingVo 交易单*/private TradingVo refundTradingVo(OrderVo orderVo){Order order = orderService.getById(orderVo.getId());//修改退款金额order.setRefund(order.getRefund().add(orderVo.getOperTionRefund()));//有退款行为order.setIsRefund(SuperConstant.YES);//防止修改分库分表分片键order.setShardingId(null);order.setOrderNo(null);boolean flag = orderService.updateById(order);//构建交易单if (flag){TradingVo tradingVo = TradingVo.builder().operTionRefund(orderVo.getOperTionRefund())//前台传递过来退款金额.tradingAmount(order.getRealAmountSum())//订单实付金额.refund(order.getRefund())//订单退款金额.isRefund(SuperConstant.YES).tradingChannel(orderVo.getTradingChannel()).tradingType(orderVo.getTradingType()).enterpriseId(orderVo.getEnterpriseId()).productOrderNo(orderVo.getOrderNo()).memo(orderVo.getTableName()+":"+orderVo.getOrderNo()).build();return tradingVo;}else {throw new ProjectException(OrderEnum.UPDATE_FAIL);}}
课堂讨论
1、订单操作流程的过程有那些?如何保证库存的处理?
2、转台操作流程的过程有哪些?为什么要对目标桌台加锁?
3、发起订单结算操作流程的过程有哪些?,为什么要根据订单生成交易单,而不直接用订单结算
4、发起订单退款需要注意那些细节?
5、为什么你们的项目在操作购物车的时候就扣库存呢?有没有更好的方案?
课后任务
1、完成订单操作、转台操作、发起结算、退款操作开发,同步到git【☆☆☆☆☆】
2、完成当天课堂讨论,同步到git【☆☆☆☆☆】
3、完成2-5道sql练习【☆☆☆☆】
4、梳理项目二-订单操作、转台操作、发起结算、退款操作业务,同步到git【☆☆☆】
