diff --git a/ghy-admin/src/main/java/com/ghy/web/controller/order/OrderDetailController.java b/ghy-admin/src/main/java/com/ghy/web/controller/order/OrderDetailController.java index bc94134c..03b4c5f1 100644 --- a/ghy-admin/src/main/java/com/ghy/web/controller/order/OrderDetailController.java +++ b/ghy-admin/src/main/java/com/ghy/web/controller/order/OrderDetailController.java @@ -36,6 +36,7 @@ import com.ghy.web.pojo.vo.OrderListResponse; import com.ghy.web.pojo.vo.OrderStandard; import com.ghy.worker.domain.Worker; import com.ghy.worker.service.WorkerService; +import com.huifu.adapay.core.exception.BaseAdaPayException; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.apache.shiro.util.Assert; import org.springframework.beans.factory.annotation.Autowired; @@ -339,6 +340,23 @@ public class OrderDetailController extends BaseController { return orderDetailService.checkOrderDetailCodeUnique(orderDetail); } + /** + * 消费者确认完成子订单 + * + * @param orderDetailId 子订单ID + */ + @PostMapping("/finish") + @ResponseBody + public AjaxResult finish(Long orderDetailId) { + try { + orderDetailService.finish(orderDetailId); + } catch (BaseAdaPayException e) { + logger.error(e.getMessage(), e); + return AjaxResult.error("操作失败,请稍后重试"); + } + return AjaxResult.success(); + } + /** * 消费者申请取消子订单 * diff --git a/ghy-order/src/main/java/com/ghy/order/mapper/OrderMasterMapper.java b/ghy-order/src/main/java/com/ghy/order/mapper/OrderMasterMapper.java index 6f289668..30c82ad0 100644 --- a/ghy-order/src/main/java/com/ghy/order/mapper/OrderMasterMapper.java +++ b/ghy-order/src/main/java/com/ghy/order/mapper/OrderMasterMapper.java @@ -77,4 +77,12 @@ public interface OrderMasterMapper { * 查询指定状态的订单 */ List selectByStatus(List status); + + /** + * 查询出未完成(orderStatus < 5)的主订单 + * + * @return 主订单 + */ + List selectUnfinished(); + } diff --git a/ghy-order/src/main/java/com/ghy/order/service/OrderDetailService.java b/ghy-order/src/main/java/com/ghy/order/service/OrderDetailService.java index a4506fa3..ace75d2c 100644 --- a/ghy-order/src/main/java/com/ghy/order/service/OrderDetailService.java +++ b/ghy-order/src/main/java/com/ghy/order/service/OrderDetailService.java @@ -1,7 +1,7 @@ package com.ghy.order.service; import com.ghy.order.domain.OrderDetail; -import com.ghy.order.domain.OrderMaster; +import com.huifu.adapay.core.exception.BaseAdaPayException; import java.util.List; import java.util.Map; @@ -98,4 +98,14 @@ public interface OrderDetailService { * 查询指定状态的订单 */ List selectByStatus(List status); + + /** + * 子订单确认完成 + * 1.修改子订单状态为 OrderStatus.FINISH.code + * 2.将子订单对应的子财务单确认分账 + * 3.异步提现至银行卡 + * + * @param orderDetailId 子订单ID + */ + void finish(Long orderDetailId) throws BaseAdaPayException; } diff --git a/ghy-order/src/main/java/com/ghy/order/service/OrderMasterService.java b/ghy-order/src/main/java/com/ghy/order/service/OrderMasterService.java index d0e8f363..c0fc6306 100644 --- a/ghy-order/src/main/java/com/ghy/order/service/OrderMasterService.java +++ b/ghy-order/src/main/java/com/ghy/order/service/OrderMasterService.java @@ -79,7 +79,11 @@ public interface OrderMasterService { int updateStatus(Long orderMasterId, int status); /** - * 确认支付订单 + * 主订单完成 + * 由定时器触发 而不是由用户触发(用户只能对子订单操作完成) + * 当所有的子订单完成后 定时器进入此方法 + * 1.修改主订单状态 + * 2.对提成、分销、罚款、手续费等类型的财务单分销和提现 * * @param orderMasterId 主订单ID */ @@ -104,4 +108,11 @@ public interface OrderMasterService { * 查询指定状态的订单 */ List selectByStatus(List status); + + /** + * 查询出未完成(orderStatus < 5)的主订单 + * + * @return 主订单 + */ + List selectUnfinished(); } diff --git a/ghy-order/src/main/java/com/ghy/order/service/impl/OrderDetailServiceImpl.java b/ghy-order/src/main/java/com/ghy/order/service/impl/OrderDetailServiceImpl.java index e6a3c416..817deb97 100644 --- a/ghy-order/src/main/java/com/ghy/order/service/impl/OrderDetailServiceImpl.java +++ b/ghy-order/src/main/java/com/ghy/order/service/impl/OrderDetailServiceImpl.java @@ -1,9 +1,13 @@ package com.ghy.order.service.impl; +import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.ghy.common.adapay.model.AdapayStatusEnum; +import com.ghy.common.adapay.model.DivMember; +import com.ghy.common.adapay.model.PaymentDTO; import com.ghy.common.constant.UserConstants; import com.ghy.common.core.text.Convert; +import com.ghy.common.enums.AdapayOrderType; import com.ghy.common.enums.OrderStatus; import com.ghy.common.enums.PayStatus; import com.ghy.common.utils.AdapayUtils; @@ -21,6 +25,7 @@ import com.ghy.payment.service.FinancialMasterService; import com.huifu.adapay.core.exception.BaseAdaPayException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.Assert; @@ -51,6 +56,8 @@ public class OrderDetailServiceImpl implements OrderDetailService { .appendValue(DAY_OF_MONTH, 2).appendValue(HOUR_OF_DAY, 2) .appendValue(MINUTE_OF_HOUR, 2).appendValue(SECOND_OF_MINUTE, 2).toFormatter(); + @Resource + private ThreadPoolTaskExecutor executor; @Resource private OrderDetailMapper orderDetailMapper; @Resource @@ -182,7 +189,7 @@ public class OrderDetailServiceImpl implements OrderDetailService { switch (orderDetail.getOrderStatus()) { case 0: case 1: // 待接单和待排期状态可直接取消 - updateStatus(orderDetailId, OrderStatus.CANCEL.code()); + orderDetailMapper.updateStatus(orderDetailId, OrderStatus.CANCEL.code()); // 发起退款 try { refund(orderDetail); @@ -191,13 +198,13 @@ public class OrderDetailServiceImpl implements OrderDetailService { } break; case 2: // 待上门状态需要师傅同意才能取消 - updateStatus(orderDetailId, OrderStatus.GOING_CANCEL.code()); + orderDetailMapper.updateStatus(orderDetailId, OrderStatus.GOING_CANCEL.code()); break; case 3: // 服务中状态需要师傅同意才能取消 - updateStatus(orderDetailId, OrderStatus.SERVER_CANCEL.code()); + orderDetailMapper.updateStatus(orderDetailId, OrderStatus.SERVER_CANCEL.code()); break; case 4: // 待确认状态需要师傅同意才能取消 - updateStatus(orderDetailId, OrderStatus.FINISH_CHECK_CANCEL.code()); + orderDetailMapper.updateStatus(orderDetailId, OrderStatus.FINISH_CHECK_CANCEL.code()); break; default: // 其他状态不可取消 throw new IllegalArgumentException("这个子订单不能取消了"); @@ -232,10 +239,6 @@ public class OrderDetailServiceImpl implements OrderDetailService { logger.info("子订单[code={}]退款成功", orderDetail.getCode()); } - private void updateStatus(Long orderDetailId, int orderStatus) { - orderDetailMapper.updateStatus(orderDetailId, orderStatus); - } - @Override @Transactional(rollbackFor = Exception.class) public void cancelAgree(Long orderDetailId, Integer agree) { @@ -244,7 +247,7 @@ public class OrderDetailServiceImpl implements OrderDetailService { if (Integer.valueOf(1).equals(agree)) { // 师傅同意取消订单 // 修改主订单状态为已取消 - updateStatus(orderDetailId, OrderStatus.CANCEL.code()); + orderDetailMapper.updateStatus(orderDetailId, OrderStatus.CANCEL.code()); // 发起退款 try { refund(orderDetail); @@ -255,7 +258,7 @@ public class OrderDetailServiceImpl implements OrderDetailService { // 师傅不同意取消订单 Integer orderStatus = orderDetail.getOrderStatus(); // 修改主订单状态为原来的状态 - updateStatus(orderDetailId, Math.abs(orderStatus)); + orderDetailMapper.updateStatus(orderDetailId, Math.abs(orderStatus)); } } @@ -269,4 +272,81 @@ public class OrderDetailServiceImpl implements OrderDetailService { Assert.isTrue(!CollectionUtils.isEmpty(status), "订单状态为空"); return orderDetailMapper.selectByStatus(status); } + + /** + * 子订单确认完成 + * 1.修改子订单状态为 OrderStatus.FINISH.code + * 2.将子订单对应的子财务单确认分账 + * 3.异步提现至银行卡 + * + * @param orderDetailId 子订单ID + */ + @Override + @Transactional(rollbackFor = Exception.class) + // 暂时先用 synchronized 来避免重复提交 + public synchronized void finish(Long orderDetailId) throws BaseAdaPayException { + // 更新订单状态 + orderDetailMapper.updateStatus(orderDetailId, OrderStatus.FINISH.code()); + // 校验订单 + OrderDetail orderDetail = selectById(orderDetailId); + Assert.notNull(orderDetail, "找不到对应的子订单"); + Assert.isTrue(orderDetail.getOrderStatus().equals(OrderStatus.FINISH.code()), "找不到对应的子订单"); + FinancialMaster financialMaster = financialMasterService.selectByOrderMasterId(orderDetail.getOrderMasterId()); + Assert.notNull(financialMaster, "找不到主财务单"); + PaymentDTO payment = financialMasterService.selectPaymentById(financialMaster.getPaymentId()); + Assert.notNull(payment, "找不到支付记录"); + FinancialDetail financialDetail = financialDetailService.selectByOrderDetailId(orderDetailId); + Assert.notNull(financialDetail, "找不到子财务单"); + Assert.isTrue(financialDetail.getPayStatus() == 1, "订单不是“已支付”状态"); + + if (BigDecimal.ZERO.compareTo(financialDetail.getPayMoney()) > -1) { + logger.info("子财务单[code={}]支付金额<=0,不需要分账", financialDetail.getCode()); + // 支付金额<=0的话 不需要走下面的流程了 + return; + } + + // 上门师傅结单 要判断一下这个单是否已退款 + if (financialDetail.getPayStatus() > 1) { + return; + } + // 分账账户ID + String memberId; + // 分账账户信息 + ArrayList divMembers = new ArrayList<>(); + String amount = AdapayUtils.bigDecimalToString(financialDetail.getPayMoney()); + memberId = AdapayUtils.getWorkerMemberId(financialDetail.getPayeeId(), orderDetail.getDeptId()); + DivMember divMember = new DivMember(memberId, amount, false); + divMembers.add(divMember); + + logger.info("子订单[code={}]分账信息: {}", orderDetail.getCode(), JSON.toJSONString(divMembers)); + JSONObject response = adapayService.paymentConfirm(orderDetail.getDeptId(), payment.getId(), payment.getOrderNo(), + amount, divMembers, null, null); + logger.info("子订单[code={}]分账结果: {}", orderDetail.getCode(), response.toJSONString()); + + boolean status = AdapayStatusEnum.pending.code.equals(response.getString("status")) || + AdapayStatusEnum.succeeded.code.equals(response.getString("status")); + // 如果确认支付失败 这里抛出异常 回滚订单状态 + Assert.isTrue(status, response.getString("error_msg")); + + // 走到这里确认分账成功了 异步进入自动提现流程 + logger.info("子订单[code={}]开始自动提现", orderDetail.getCode()); + divMembers.forEach(member -> executor.execute(() -> { + try { + String orderNo = AdapayUtils.createOrderNo(AdapayOrderType.DRAW_CASH); + JSONObject drawCashResponse = adapayService.drawCash(orderDetail.getDeptId(), orderNo, "T1", + amount, memberId, "订单结算", null); + + boolean drawCashStatus = AdapayStatusEnum.pending.code.equals(drawCashResponse.getString("status")) || + AdapayStatusEnum.succeeded.code.equals(drawCashResponse.getString("status")); + + if (!drawCashStatus) { + //如果提现失败 把信息记录到error日志里 + logger.error("自动发起提现失败: deptId={}, memberId={}, amount={}, 失败原因:{}", orderDetail.getDeptId(), + memberId, amount, drawCashResponse.getString("error_msg")); + } + } catch (BaseAdaPayException e) { + logger.error("自动发起提现失败: orderDetailId={}, memberId={}, cashAmt={}", orderDetailId, memberId, amount, e); + } + })); + } } diff --git a/ghy-order/src/main/java/com/ghy/order/service/impl/OrderMasterServiceImpl.java b/ghy-order/src/main/java/com/ghy/order/service/impl/OrderMasterServiceImpl.java index 43470ba2..d2819ca2 100644 --- a/ghy-order/src/main/java/com/ghy/order/service/impl/OrderMasterServiceImpl.java +++ b/ghy-order/src/main/java/com/ghy/order/service/impl/OrderMasterServiceImpl.java @@ -83,10 +83,6 @@ public class OrderMasterServiceImpl implements OrderMasterService { @Override public int updateOrderMaster(OrderMaster orderMaster) throws BaseAdaPayException { - if (orderMaster.getOrderStatus().equals(OrderStatus.FINISH.code())) { - logger.info("订单[ID={}]完成,进入确认分账", orderMaster.getId()); - finish(orderMaster.getId()); - } return orderMasterMapper.updateOrderMaster(orderMaster); } @@ -131,17 +127,39 @@ public class OrderMasterServiceImpl implements OrderMasterService { return orderMasterMapper.updateStatus(orderMasterId, orderStatus); } + /** + * 主订单完成 + * 由定时器触发 而不是由用户触发(用户只能对子订单操作完成) + * 当所有的子订单完成后 定时器进入此方法 + * 1.修改主订单状态 + * 2.对提成、分销、罚款、手续费等类型的财务单分销和提现 + * + * @param orderMasterId 主订单ID + */ @Override @Transactional(rollbackFor = Exception.class) - public void finish(Long orderMasterId) throws BaseAdaPayException { + // 暂时先用 synchronized 来避免重复提交 + public synchronized void finish(Long orderMasterId) throws BaseAdaPayException { // 校验订单 OrderMaster orderMaster = selectById(orderMasterId); - Assert.notNull(orderMaster, "找不到对应的订单"); + Assert.notNull(orderMaster, "订单不存在"); + List orderDetails = orderDetailService.selectByOrderMasterId(orderMasterId); + for (OrderDetail orderDetail : orderDetails) { + logger.info("主订单[id={}]存在未完成的子订单", orderMasterId); + if (orderDetail.getOrderStatus() < 5) { + return; + } + } FinancialMaster financialMaster = financialMasterService.selectByOrderMasterId(orderMasterId); - Assert.notNull(financialMaster, "找不到订单"); - Assert.isTrue(financialMaster.getPayStatus() == 1, "订单未支付"); + Assert.notNull(financialMaster, String.format("主订单[id=%d]找不到对应的主财务单", orderMasterId)); + if (financialMaster.getPayStatus() != 1) { + logger.info("主订单[id={}]未支付", orderMasterId); + return; + } PaymentDTO payment = financialMasterService.selectPaymentById(financialMaster.getPaymentId()); - Assert.notNull(payment, "找不到支付记录"); + Assert.notNull(payment, String.format("主订单[id=%d]找不到支付记录", orderMasterId)); + + updateStatus(orderMasterId, OrderStatus.FINISH_CHECK.code()); if (BigDecimal.ZERO.compareTo(financialMaster.getPayMoney()) > -1) { logger.info("订单[code={}]支付金额<=0,不需要分账", orderMaster.getCode()); @@ -153,7 +171,7 @@ public class OrderMasterServiceImpl implements OrderMasterService { List financialDetails = financialDetailService.selectByFinancialMasterId(financialMaster.getId()); // 校验金额 主财务单的金额减去所有子财务单的金额是否=0 BigDecimal checkMoney = financialMaster.getPayMoney(); - // 确认支付金额 = 主财务单付款金额 - 退款金额 + // 确认支付金额 = 主财务单付款金额 - 退款金额 - 已确认金额 BigDecimal confirmAmt = financialMaster.getPayMoney(); // key:memberId(分账账户ID) value:分账金额 @@ -164,11 +182,9 @@ public class OrderMasterServiceImpl implements OrderMasterService { String memberId; switch (financialDetail.getFinancialDetailType()) { case 0: - // 上门师傅结单 要判断一下这个单是否已退款 - if (financialDetail.getPayStatus() < 2) { - memberId = AdapayUtils.getWorkerMemberId(financialDetail.getPayeeId(), orderMaster.getDeptId()); - memberMap.merge(memberId, financialDetail.getPayMoney(), BigDecimal::add); - } + // 上门师傅的财务单单独确认分账了 不在这里分 + // 减掉已确认金额 + confirmAmt = confirmAmt.subtract(financialDetail.getPayMoney()); break; case 1: // 大师傅/店铺提成 @@ -192,7 +208,7 @@ public class OrderMasterServiceImpl implements OrderMasterService { } break; case 4: - // 退款 + // 减掉退款金额 confirmAmt = confirmAmt.subtract(financialDetail.getPayMoney()); case 5: // 订单超时罚金 归平台所有 @@ -204,7 +220,7 @@ public class OrderMasterServiceImpl implements OrderMasterService { } // 这里校验一次主财务单的金额减去所有子财务单的金额是否=0 - Assert.isTrue(BigDecimal.ZERO.compareTo(checkMoney) == 0, "订单异常,请稍后再试"); + Assert.isTrue(BigDecimal.ZERO.compareTo(checkMoney) == 0, "订单金额异常,请稍后再试"); // 分账账户 ArrayList divMembers = new ArrayList<>(); @@ -384,4 +400,9 @@ public class OrderMasterServiceImpl implements OrderMasterService { Assert.isTrue(status, response.getString("error_msg")); logger.info("订单[code={}]退款成功", orderMaster.getCode()); } + + @Override + public List selectUnfinished() { + return orderMasterMapper.selectUnfinished(); + } } diff --git a/ghy-order/src/main/resources/mapper/order/OrderMasterMapper.xml b/ghy-order/src/main/resources/mapper/order/OrderMasterMapper.xml index be3c824c..8deb433c 100644 --- a/ghy-order/src/main/resources/mapper/order/OrderMasterMapper.xml +++ b/ghy-order/src/main/resources/mapper/order/OrderMasterMapper.xml @@ -254,7 +254,7 @@ - WHERE `order_status` IN @@ -270,4 +270,9 @@ + + diff --git a/ghy-quartz/src/main/java/com/ghy/quartz/service/impl/OrderServiceImpl.java b/ghy-quartz/src/main/java/com/ghy/quartz/service/impl/OrderServiceImpl.java index 0dd622c2..aa80ef03 100644 --- a/ghy-quartz/src/main/java/com/ghy/quartz/service/impl/OrderServiceImpl.java +++ b/ghy-quartz/src/main/java/com/ghy/quartz/service/impl/OrderServiceImpl.java @@ -24,6 +24,7 @@ import javax.annotation.Resource; import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.Calendar; +import java.util.Collections; import java.util.Date; import java.util.List; @@ -121,24 +122,33 @@ public class OrderServiceImpl implements OrderService { @Override public void finishOrder() { - OrderMaster orderMaster = new OrderMaster(); - orderMaster.setOrderStatus(OrderStatus.FINISH_CHECK.code()); - List orders = orderMasterService.selectOrderMasterList(orderMaster); + // 查询出"待确认"状态的子订单 + List orderDetails = orderDetailService.selectByStatus(Collections.singletonList(OrderStatus.FINISH_CHECK.code())); long now = System.currentTimeMillis(); long day14ago = now - (14 * 24 * 60 * 60 * 1000L); - for (OrderMaster order : orders) { + for (OrderDetail orderDetail : orderDetails) { // 查询符合自动确认的订单 - if (day14ago > order.getUpdateTime().getTime()) { - logger.info("订单自动完成[id={}, code={}]", order.getId(), order.getCode()); + if (day14ago > orderDetail.getUpdateTime().getTime()) { + logger.info("订单自动完成[id={}, code={}]", orderDetail.getId(), orderDetail.getCode()); try { // 完单流程(分账与提现) - orderMasterService.finish(order.getId()); - // 修改订单状态 - orderMasterService.updateStatus(order.getId(), OrderStatus.FINISH.code()); + orderDetailService.finish(orderDetail.getId()); } catch (BaseAdaPayException e) { - logger.error("订单自动完成[id={}, code={}]出错", order.getId(), order.getCode(), e); + logger.error("订单自动完成[id={}, code={}]出错", orderDetail.getId(), orderDetail.getCode(), e); } } } + + // 当所有的子订单完成后 自动把主订单完成 + List orderMasters = orderMasterService.selectUnfinished(); + for (OrderMaster orderMaster : orderMasters) { + executor.execute(() -> { + try { + orderMasterService.finish(orderMaster.getId()); + }catch (Exception e){ + logger.error("主订单 Finish 失败", e); + } + }); + } } }