|
@@ -0,0 +1,606 @@
|
|
|
|
|
+package com.ruoyi.app.order;
|
|
|
|
|
+
|
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
|
|
|
|
+import com.ruoyi.app.order.dto.AdminOrderActionRequest;
|
|
|
|
|
+import com.ruoyi.app.order.dto.AdminOrderStatusContext;
|
|
|
|
|
+import com.ruoyi.app.order.dto.AdminOrderStatusUpdateRequest;
|
|
|
|
|
+import com.ruoyi.common.exception.ServiceException;
|
|
|
|
|
+import com.ruoyi.system.domain.PointsTransaction;
|
|
|
|
|
+import com.ruoyi.system.domain.PosOrder;
|
|
|
|
|
+import com.ruoyi.system.domain.PosOrderOmgPayment;
|
|
|
|
|
+import com.ruoyi.system.domain.PosOrderOmgRefund;
|
|
|
|
|
+import com.ruoyi.system.domain.UserWallet;
|
|
|
|
|
+import com.ruoyi.system.service.IPosOrderOmgPaymentService;
|
|
|
|
|
+import com.ruoyi.system.service.IPosOrderOmgRefundService;
|
|
|
|
|
+import com.ruoyi.system.service.IPosOrderService;
|
|
|
|
|
+import com.ruoyi.system.service.IPointsTransactionService;
|
|
|
|
|
+import com.ruoyi.system.service.IUserWalletService;
|
|
|
|
|
+import com.ruoyi.system.utils.OrderLogHelper;
|
|
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
+
|
|
|
|
|
+import java.util.ArrayList;
|
|
|
|
|
+import java.util.Date;
|
|
|
|
|
+import java.util.List;
|
|
|
|
|
+import java.util.Objects;
|
|
|
|
|
+
|
|
|
|
|
+@Service
|
|
|
|
|
+public class OrderLifecycleService {
|
|
|
|
|
+
|
|
|
|
|
+ public static final String PAY_TYPE_OFFLINE = "1";
|
|
|
|
|
+ public static final String PAY_TYPE_OMG = "7";
|
|
|
|
|
+
|
|
|
|
|
+ private final IPosOrderService posOrderService;
|
|
|
|
|
+ private final OrderService billingService;
|
|
|
|
|
+ private final OrderLogHelper orderLogHelper;
|
|
|
|
|
+ private final IUserWalletService userWalletService;
|
|
|
|
|
+ private final IPointsTransactionService pointsTransactionService;
|
|
|
|
|
+ private final IPosOrderOmgPaymentService omgPaymentService;
|
|
|
|
|
+ private final IPosOrderOmgRefundService omgRefundService;
|
|
|
|
|
+
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ public OrderLifecycleService(IPosOrderService posOrderService,
|
|
|
|
|
+ OrderService billingService,
|
|
|
|
|
+ OrderLogHelper orderLogHelper,
|
|
|
|
|
+ IUserWalletService userWalletService,
|
|
|
|
|
+ IPointsTransactionService pointsTransactionService,
|
|
|
|
|
+ IPosOrderOmgPaymentService omgPaymentService,
|
|
|
|
|
+ IPosOrderOmgRefundService omgRefundService) {
|
|
|
|
|
+ this.posOrderService = posOrderService;
|
|
|
|
|
+ this.billingService = billingService;
|
|
|
|
|
+ this.orderLogHelper = orderLogHelper;
|
|
|
|
|
+ this.userWalletService = userWalletService;
|
|
|
|
|
+ this.pointsTransactionService = pointsTransactionService;
|
|
|
|
|
+ this.omgPaymentService = omgPaymentService;
|
|
|
|
|
+ this.omgRefundService = omgRefundService;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public AdminOrderStatusContext getStatusContext(Long id) {
|
|
|
|
|
+ return buildContext(requireOrder(id));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public AdminOrderStatusContext validateOmgReconcile(Long id, AdminOrderActionRequest request) {
|
|
|
|
|
+ requireReason(request);
|
|
|
|
|
+ PosOrder order = requireOrder(id);
|
|
|
|
|
+ validateSnapshot(order, request);
|
|
|
|
|
+ if (!PAY_TYPE_OMG.equals(order.getPayType()) || order.getPayStatus() != 0L
|
|
|
|
|
+ || order.getState() == 4L || order.getAfterSaleStatus() != 0L) {
|
|
|
|
|
+ throw new ServiceException("当前订单状态不允许 OMG 支付补单");
|
|
|
|
|
+ }
|
|
|
|
|
+ PosOrderOmgPayment payment = omgPaymentService.getLatestByDdId(String.valueOf(order.getDdId()));
|
|
|
|
|
+ if (payment == null || !Integer.valueOf(0).equals(payment.getPayStatus())) {
|
|
|
|
|
+ throw new ServiceException("没有可查询的 OMG 未支付流水");
|
|
|
|
|
+ }
|
|
|
|
|
+ return buildContext(order);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public AdminOrderStatusContext validateOmgRefund(Long id, AdminOrderActionRequest request) {
|
|
|
|
|
+ requireReason(request);
|
|
|
|
|
+ PosOrder order = requireOrder(id);
|
|
|
|
|
+ validateSnapshot(order, request);
|
|
|
|
|
+ if (!PAY_TYPE_OMG.equals(order.getPayType())) {
|
|
|
|
|
+ throw new ServiceException("该订单非 OMG 支付");
|
|
|
|
|
+ }
|
|
|
|
|
+ validateRefundableOrder(order);
|
|
|
|
|
+ PosOrderOmgPayment payment = omgPaymentService.getLatestByDdId(String.valueOf(order.getDdId()));
|
|
|
|
|
+ if (payment == null || (!Integer.valueOf(1).equals(payment.getPayStatus())
|
|
|
|
|
+ && !Integer.valueOf(3).equals(payment.getPayStatus()))) {
|
|
|
|
|
+ throw new ServiceException("OMG 支付流水状态不允许退款");
|
|
|
|
|
+ }
|
|
|
|
|
+ return buildContext(order);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public void auditExternalAction(Long id, AdminOrderActionRequest request,
|
|
|
|
|
+ Long operatorId, String operatorName, String action) {
|
|
|
|
|
+ PosOrder order = requireOrder(id);
|
|
|
|
|
+ logStatusChange(order, request, operatorId, operatorName,
|
|
|
|
|
+ request.getExpectedState(), request.getExpectedDeliveryStatus(),
|
|
|
|
|
+ request.getExpectedPayStatus(), request.getExpectedAfterSaleStatus(), action);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
|
|
+ public AdminOrderStatusContext updateStatus(Long id, AdminOrderStatusUpdateRequest request,
|
|
|
|
|
+ Long operatorId, String operatorName) {
|
|
|
|
|
+ requireReason(request);
|
|
|
|
|
+ PosOrder order = requireOrder(id);
|
|
|
|
|
+ validateSnapshot(order, request);
|
|
|
|
|
+ rejectNormalAdjustmentWhenBlocked(order);
|
|
|
|
|
+
|
|
|
|
|
+ Long targetState = request.getTargetState();
|
|
|
|
|
+ Long targetDelivery = request.getTargetDeliveryStatus();
|
|
|
|
|
+ if (targetState == null && targetDelivery == null) {
|
|
|
|
|
+ throw new ServiceException("请选择需要调整的状态");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Long nextState = order.getState();
|
|
|
|
|
+ Long nextDelivery = order.getDeliveryStatus();
|
|
|
|
|
+ Date deliveredAt = order.getSdTime();
|
|
|
|
|
+
|
|
|
|
|
+ if (targetDelivery != null && !Objects.equals(targetDelivery, order.getDeliveryStatus())) {
|
|
|
|
|
+ validateDeliveryTransition(order, targetDelivery);
|
|
|
|
|
+ nextDelivery = targetDelivery;
|
|
|
|
|
+ if (targetDelivery == 3L) {
|
|
|
|
|
+ nextState = 3L;
|
|
|
|
|
+ deliveredAt = new Date();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (targetState != null && !Objects.equals(targetState, order.getState())) {
|
|
|
|
|
+ validateOrderTransition(order, targetState, nextDelivery);
|
|
|
|
|
+ nextState = targetState;
|
|
|
|
|
+ if (order.getState() == 1L && targetState == 2L
|
|
|
|
|
+ && order.getType() == 0L && nextDelivery == null) {
|
|
|
|
|
+ nextDelivery = 0L;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (Objects.equals(nextState, order.getState())
|
|
|
|
|
+ && Objects.equals(nextDelivery, order.getDeliveryStatus())) {
|
|
|
|
|
+ return buildContext(order);
|
|
|
|
|
+ }
|
|
|
|
|
+ validateResultInvariants(order, nextState, nextDelivery, order.getPayStatus(), order.getAfterSaleStatus());
|
|
|
|
|
+
|
|
|
|
|
+ PosOrder update = new PosOrder();
|
|
|
|
|
+ update.setState(nextState);
|
|
|
|
|
+ update.setDeliveryStatus(nextDelivery);
|
|
|
|
|
+ if (nextState == 3L && order.getState() != 3L) {
|
|
|
|
|
+ update.setSdTime(deliveredAt == null ? new Date() : deliveredAt);
|
|
|
|
|
+ }
|
|
|
|
|
+ applyCas(order, update);
|
|
|
|
|
+
|
|
|
|
|
+ order.setState(nextState);
|
|
|
|
|
+ order.setDeliveryStatus(nextDelivery);
|
|
|
|
|
+ if (update.getSdTime() != null) {
|
|
|
|
|
+ order.setSdTime(update.getSdTime());
|
|
|
|
|
+ }
|
|
|
|
|
+ if (nextState == 3L) {
|
|
|
|
|
+ completeSideEffects(order);
|
|
|
|
|
+ }
|
|
|
|
|
+ logStatusChange(order, request, operatorId, operatorName,
|
|
|
|
|
+ request.getExpectedState(), request.getExpectedDeliveryStatus(),
|
|
|
|
|
+ request.getExpectedPayStatus(), request.getExpectedAfterSaleStatus(),
|
|
|
|
|
+ "平台调整[订单/配送]");
|
|
|
|
|
+ return buildContext(order);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
|
|
+ public AdminOrderStatusContext confirmOfflinePayment(Long id, AdminOrderActionRequest request,
|
|
|
|
|
+ Long operatorId, String operatorName) {
|
|
|
|
|
+ requireReason(request);
|
|
|
|
|
+ PosOrder order = requireOrder(id);
|
|
|
|
|
+ validateSnapshot(order, request);
|
|
|
|
|
+ if (!PAY_TYPE_OFFLINE.equals(order.getPayType())) {
|
|
|
|
|
+ throw new ServiceException("仅线下支付订单可以确认收款,OMG 订单请使用支付补单");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (order.getPayStatus() != 0L || order.getState() == 3L || order.getState() == 4L
|
|
|
|
|
+ || order.getAfterSaleStatus() != 0L) {
|
|
|
|
|
+ throw new ServiceException("当前订单状态不允许确认线下收款");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ PosOrder update = new PosOrder();
|
|
|
|
|
+ update.setPayStatus(1L);
|
|
|
|
|
+ applyCas(order, update);
|
|
|
|
|
+ order.setPayStatus(1L);
|
|
|
|
|
+ logStatusChange(order, request, operatorId, operatorName,
|
|
|
|
|
+ request.getExpectedState(), request.getExpectedDeliveryStatus(),
|
|
|
|
|
+ request.getExpectedPayStatus(), request.getExpectedAfterSaleStatus(),
|
|
|
|
|
+ "平台确认线下收款");
|
|
|
|
|
+ return buildContext(order);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
|
|
+ public AdminOrderStatusContext confirmOfflineRefund(Long id, AdminOrderActionRequest request,
|
|
|
|
|
+ Long operatorId, String operatorName) {
|
|
|
|
|
+ requireReason(request);
|
|
|
|
|
+ PosOrder order = requireOrder(id);
|
|
|
|
|
+ validateSnapshot(order, request);
|
|
|
|
|
+ if (!PAY_TYPE_OFFLINE.equals(order.getPayType())) {
|
|
|
|
|
+ throw new ServiceException("仅线下支付订单可以确认线下退款");
|
|
|
|
|
+ }
|
|
|
|
|
+ validateRefundableOrder(order);
|
|
|
|
|
+
|
|
|
|
|
+ PosOrder update = refundedOrderUpdate();
|
|
|
|
|
+ applyCas(order, update);
|
|
|
|
|
+ order.setState(4L);
|
|
|
|
|
+ order.setPayStatus(2L);
|
|
|
|
|
+ order.setAfterSaleStatus(3L);
|
|
|
|
|
+ refundPoints(order);
|
|
|
|
|
+ logStatusChange(order, request, operatorId, operatorName,
|
|
|
|
|
+ request.getExpectedState(), request.getExpectedDeliveryStatus(),
|
|
|
|
|
+ request.getExpectedPayStatus(), request.getExpectedAfterSaleStatus(),
|
|
|
|
|
+ "平台确认线下退款");
|
|
|
|
|
+ return buildContext(order);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
|
|
+ public AdminOrderStatusContext finalizeOmgRefund(Long id, AdminOrderActionRequest request,
|
|
|
|
|
+ Long operatorId, String operatorName,
|
|
|
|
|
+ String action) {
|
|
|
|
|
+ requireReason(request);
|
|
|
|
|
+ PosOrder order = requireOrder(id);
|
|
|
|
|
+ validateSnapshot(order, request);
|
|
|
|
|
+ if (!PAY_TYPE_OMG.equals(order.getPayType())) {
|
|
|
|
|
+ throw new ServiceException("该订单非 OMG 支付");
|
|
|
|
|
+ }
|
|
|
|
|
+ validateRefundableOrder(order);
|
|
|
|
|
+
|
|
|
|
|
+ PosOrder update = refundedOrderUpdate();
|
|
|
|
|
+ applyCas(order, update);
|
|
|
|
|
+ order.setState(4L);
|
|
|
|
|
+ order.setPayStatus(2L);
|
|
|
|
|
+ order.setAfterSaleStatus(3L);
|
|
|
|
|
+ refundPoints(order);
|
|
|
|
|
+ logStatusChange(order, request, operatorId, operatorName,
|
|
|
|
|
+ request.getExpectedState(), request.getExpectedDeliveryStatus(),
|
|
|
|
|
+ request.getExpectedPayStatus(), request.getExpectedAfterSaleStatus(), action);
|
|
|
|
|
+ return buildContext(order);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
|
|
+ public AdminOrderStatusContext finalizeSystemOmgRefund(Long id) {
|
|
|
|
|
+ PosOrder order = requireOrder(id);
|
|
|
|
|
+ if (order.getState() == 4L && order.getPayStatus() == 2L && order.getAfterSaleStatus() == 3L) {
|
|
|
|
|
+ return buildContext(order);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!PAY_TYPE_OMG.equals(order.getPayType()) || order.getPayStatus() != 1L
|
|
|
|
|
+ || order.getAfterSaleStatus() != 0L || order.getState() == 3L) {
|
|
|
|
|
+ throw new ServiceException("OMG 退款后的订单状态不允许同步");
|
|
|
|
|
+ }
|
|
|
|
|
+ PosOrder update = refundedOrderUpdate();
|
|
|
|
|
+ applyCas(order, update);
|
|
|
|
|
+ Long beforeState = order.getState();
|
|
|
|
|
+ order.setState(4L);
|
|
|
|
|
+ order.setPayStatus(2L);
|
|
|
|
|
+ order.setAfterSaleStatus(3L);
|
|
|
|
|
+ refundPoints(order);
|
|
|
|
|
+ orderLogHelper.logSync(String.valueOf(order.getDdId()), 0, null, "系统",
|
|
|
|
|
+ "OMG退款同步:state " + beforeState + "→4,pay 1→2,afterSale 0→3");
|
|
|
|
|
+ return buildContext(order);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
|
|
+ public AdminOrderStatusContext completeDeliveryByRider(Long id, Long riderId, String riderName) {
|
|
|
|
|
+ return completeDeliveryByRider(id, riderId, riderName, null);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
|
|
+ public AdminOrderStatusContext completeDeliveryByRider(Long id, Long riderId, String riderName,
|
|
|
|
|
+ String riderImage) {
|
|
|
|
|
+ PosOrder order = requireOrder(id);
|
|
|
|
|
+ if (order.getType() != 0L || order.getState() != 2L || order.getDeliveryStatus() == null
|
|
|
|
|
+ || order.getDeliveryStatus() != 2L || order.getAfterSaleStatus() != 0L
|
|
|
|
|
+ || order.getQsId() == null || !order.getQsId().equals(riderId)) {
|
|
|
|
|
+ throw new ServiceException("当前订单状态不允许确认送达");
|
|
|
|
|
+ }
|
|
|
|
|
+ Long nextPayStatus = order.getPayStatus();
|
|
|
|
|
+ if (nextPayStatus != 1L) {
|
|
|
|
|
+ if (nextPayStatus == 0L && (PAY_TYPE_OFFLINE.equals(order.getPayType())
|
|
|
|
|
+ || "1".equals(order.getCollectPayment()))) {
|
|
|
|
|
+ nextPayStatus = 1L;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ throw new ServiceException("订单尚未支付,不能确认送达");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ PosOrder update = new PosOrder();
|
|
|
|
|
+ update.setState(3L);
|
|
|
|
|
+ update.setDeliveryStatus(3L);
|
|
|
|
|
+ update.setPayStatus(nextPayStatus);
|
|
|
|
|
+ update.setSdTime(new Date());
|
|
|
|
|
+ if (riderImage != null && !riderImage.isBlank()) {
|
|
|
|
|
+ update.setQsImg(riderImage);
|
|
|
|
|
+ }
|
|
|
|
|
+ applyCas(order, update);
|
|
|
|
|
+ Long oldPayStatus = order.getPayStatus();
|
|
|
|
|
+ order.setState(3L);
|
|
|
|
|
+ order.setDeliveryStatus(3L);
|
|
|
|
|
+ order.setPayStatus(nextPayStatus);
|
|
|
|
|
+ order.setSdTime(update.getSdTime());
|
|
|
|
|
+ completeSideEffects(order);
|
|
|
|
|
+ orderLogHelper.logSync(String.valueOf(order.getDdId()), 3, riderId, safeName(riderName),
|
|
|
|
|
+ "骑手确认送达:state 2→3,delivery 2→3,pay " + oldPayStatus + "→" + nextPayStatus);
|
|
|
|
|
+ return buildContext(order);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private PosOrder refundedOrderUpdate() {
|
|
|
|
|
+ PosOrder update = new PosOrder();
|
|
|
|
|
+ update.setState(4L);
|
|
|
|
|
+ update.setPayStatus(2L);
|
|
|
|
|
+ update.setAfterSaleStatus(3L);
|
|
|
|
|
+ return update;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void validateRefundableOrder(PosOrder order) {
|
|
|
|
|
+ if (order.getState() == 3L) {
|
|
|
|
|
+ throw new ServiceException("已完成订单需结算冲正,本期不支持退款");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (order.getState() == 4L || order.getPayStatus() != 1L || order.getAfterSaleStatus() != 0L) {
|
|
|
|
|
+ throw new ServiceException("当前订单状态不允许退款");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void rejectNormalAdjustmentWhenBlocked(PosOrder order) {
|
|
|
|
|
+ if (order.getState() == 3L || order.getState() == 4L || order.getPayStatus() == 2L) {
|
|
|
|
|
+ throw new ServiceException("终态订单不能重新调整状态");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (order.getAfterSaleStatus() != 0L) {
|
|
|
|
|
+ throw new ServiceException("订单存在售后流程,请先处理售后");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void validateOrderTransition(PosOrder order, Long targetState, Long effectiveDelivery) {
|
|
|
|
|
+ Long current = order.getState();
|
|
|
|
|
+ boolean valid = current == 0L && targetState == 1L
|
|
|
|
|
+ || current == 1L && targetState == 2L
|
|
|
|
|
+ || current == 2L && targetState == 3L
|
|
|
|
|
+ || (current == 0L || current == 1L) && targetState == 4L;
|
|
|
|
|
+ if (!valid) {
|
|
|
|
|
+ throw new ServiceException("订单状态只能按规则向前推进");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (current == 0L && targetState == 1L
|
|
|
|
|
+ && PAY_TYPE_OMG.equals(order.getPayType()) && order.getPayStatus() != 1L) {
|
|
|
|
|
+ throw new ServiceException("OMG 订单支付成功后才能接单");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (targetState == 4L && order.getPayStatus() != 0L) {
|
|
|
|
|
+ throw new ServiceException("已支付订单请使用退款操作");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (targetState == 3L) {
|
|
|
|
|
+ if (order.getPayStatus() != 1L) {
|
|
|
|
|
+ throw new ServiceException("订单支付完成后才能完成");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (order.getType() == 0L && !Long.valueOf(3L).equals(effectiveDelivery)) {
|
|
|
|
|
+ throw new ServiceException("外送订单送达后才能完成");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void validateDeliveryTransition(PosOrder order, Long targetDelivery) {
|
|
|
|
|
+ if (order.getType() != 0L) {
|
|
|
|
|
+ throw new ServiceException("自取或堂食订单不能修改配送状态");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (order.getState() != 2L) {
|
|
|
|
|
+ throw new ServiceException("订单出餐后才能推进配送状态");
|
|
|
|
|
+ }
|
|
|
|
|
+ Long current = order.getDeliveryStatus();
|
|
|
|
|
+ boolean valid = current == null && targetDelivery == 0L
|
|
|
|
|
+ || current != null && targetDelivery == current + 1L;
|
|
|
|
|
+ if (!valid) {
|
|
|
|
|
+ throw new ServiceException("配送状态只能逐步向前推进");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (targetDelivery >= 1L && order.getQsId() == null) {
|
|
|
|
|
+ throw new ServiceException("订单尚未指派骑手");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (targetDelivery == 3L && order.getPayStatus() != 1L) {
|
|
|
|
|
+ throw new ServiceException("订单支付完成后才能确认送达");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void validateResultInvariants(PosOrder order, Long state, Long delivery,
|
|
|
|
|
+ Long payStatus, Long afterSaleStatus) {
|
|
|
|
|
+ if (order.getType() != 0L && delivery != null) {
|
|
|
|
|
+ throw new ServiceException("非外送订单不能存在配送状态");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (delivery != null && delivery == 3L && state != 3L) {
|
|
|
|
|
+ throw new ServiceException("送达订单必须同步完成");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (state == 3L && (payStatus != 1L || afterSaleStatus != 0L)) {
|
|
|
|
|
+ throw new ServiceException("订单状态组合不合法");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void validateSnapshot(PosOrder order, AdminOrderActionRequest request) {
|
|
|
|
|
+ if (!Objects.equals(order.getState(), request.getExpectedState())
|
|
|
|
|
+ || !Objects.equals(order.getDeliveryStatus(), request.getExpectedDeliveryStatus())
|
|
|
|
|
+ || !Objects.equals(order.getPayStatus(), request.getExpectedPayStatus())
|
|
|
|
|
+ || !Objects.equals(order.getAfterSaleStatus(), request.getExpectedAfterSaleStatus())) {
|
|
|
|
|
+ throw conflict();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void applyCas(PosOrder order, PosOrder update) {
|
|
|
|
|
+ LambdaUpdateWrapper<PosOrder> wrapper = new LambdaUpdateWrapper<PosOrder>()
|
|
|
|
|
+ .eq(PosOrder::getId, order.getId())
|
|
|
|
|
+ .eq(PosOrder::getState, order.getState())
|
|
|
|
|
+ .eq(PosOrder::getPayStatus, order.getPayStatus())
|
|
|
|
|
+ .eq(PosOrder::getAfterSaleStatus, order.getAfterSaleStatus());
|
|
|
|
|
+ if (order.getDeliveryStatus() == null) {
|
|
|
|
|
+ wrapper.isNull(PosOrder::getDeliveryStatus);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ wrapper.eq(PosOrder::getDeliveryStatus, order.getDeliveryStatus());
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!posOrderService.update(update, wrapper)) {
|
|
|
|
|
+ throw conflict();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private ServiceException conflict() {
|
|
|
|
|
+ return new ServiceException("订单状态已变化,请刷新后重试");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void completeSideEffects(PosOrder order) {
|
|
|
|
|
+ billingService.setSanghuBilling(order);
|
|
|
|
|
+ if (order.getType() == 0L && order.getQsId() != null) {
|
|
|
|
|
+ billingService.setQishouBilling(order);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void refundPoints(PosOrder order) {
|
|
|
|
|
+ if (order.getPoints() == null || order.getPoints() <= 0) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ PointsTransaction existing = pointsTransactionService.getOne(
|
|
|
|
|
+ new LambdaQueryWrapper<PointsTransaction>()
|
|
|
|
|
+ .eq(PointsTransaction::getDdId, String.valueOf(order.getDdId()))
|
|
|
|
|
+ .eq(PointsTransaction::getType, "2"));
|
|
|
|
|
+ if (existing != null) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ UserWallet wallet = userWalletService.getOne(new LambdaQueryWrapper<UserWallet>()
|
|
|
|
|
+ .eq(UserWallet::getUserId, order.getUserId())
|
|
|
|
|
+ .isNull(UserWallet::getStoreId));
|
|
|
|
|
+ if (wallet == null || wallet.getPointsWallet() == null) {
|
|
|
|
|
+ throw new ServiceException("用户积分钱包不存在");
|
|
|
|
|
+ }
|
|
|
|
|
+ int oldVersion = wallet.getVersion();
|
|
|
|
|
+ wallet.setPointsWallet(wallet.getPointsWallet() + order.getPoints());
|
|
|
|
|
+ wallet.setVersion(oldVersion + 1);
|
|
|
|
|
+ boolean updated = userWalletService.update(wallet, new LambdaQueryWrapper<UserWallet>()
|
|
|
|
|
+ .eq(UserWallet::getId, wallet.getId())
|
|
|
|
|
+ .eq(UserWallet::getVersion, oldVersion));
|
|
|
|
|
+ if (!updated) {
|
|
|
|
|
+ throw new ServiceException("积分返还冲突,请稍后重试");
|
|
|
|
|
+ }
|
|
|
|
|
+ PointsTransaction transaction = new PointsTransaction();
|
|
|
|
|
+ transaction.setUserId(order.getUserId());
|
|
|
|
|
+ transaction.setPointsChange("+" + order.getPoints());
|
|
|
|
|
+ transaction.setCurrentPoints(String.valueOf(wallet.getPointsWallet()));
|
|
|
|
|
+ transaction.setDdId(String.valueOf(order.getDdId()));
|
|
|
|
|
+ transaction.setType("2");
|
|
|
|
|
+ transaction.setCreateTime(new Date());
|
|
|
|
|
+ pointsTransactionService.save(transaction);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void logStatusChange(PosOrder after, AdminOrderActionRequest request,
|
|
|
|
|
+ Long operatorId, String operatorName,
|
|
|
|
|
+ Long beforeState, Long beforeDelivery, Long beforePay, Long beforeAfterSale,
|
|
|
|
|
+ String action) {
|
|
|
|
|
+ String content = action + ":state " + value(beforeState) + "→" + value(after.getState())
|
|
|
|
|
+ + ",delivery " + value(beforeDelivery) + "→" + value(after.getDeliveryStatus())
|
|
|
|
|
+ + ",pay " + value(beforePay) + "→" + value(after.getPayStatus())
|
|
|
|
|
+ + ",afterSale " + value(beforeAfterSale) + "→" + value(after.getAfterSaleStatus())
|
|
|
|
|
+ + ";原因:" + request.normalizedReason();
|
|
|
|
|
+ orderLogHelper.logSync(String.valueOf(after.getDdId()), 1, operatorId,
|
|
|
|
|
+ safeName(operatorName), content);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private AdminOrderStatusContext buildContext(PosOrder order) {
|
|
|
|
|
+ AdminOrderStatusContext context = new AdminOrderStatusContext();
|
|
|
|
|
+ context.setId(order.getId());
|
|
|
|
|
+ context.setDdId(String.valueOf(order.getDdId()));
|
|
|
|
|
+ context.setType(order.getType());
|
|
|
|
|
+ context.setPayType(order.getPayType());
|
|
|
|
|
+ context.setState(order.getState());
|
|
|
|
|
+ context.setDeliveryStatus(order.getDeliveryStatus());
|
|
|
|
|
+ context.setPayStatus(order.getPayStatus());
|
|
|
|
|
+ context.setAfterSaleStatus(order.getAfterSaleStatus());
|
|
|
|
|
+ context.setSdTime(order.getSdTime());
|
|
|
|
|
+ context.setRiderAssigned(order.getQsId() != null);
|
|
|
|
|
+ context.setAllowedOrderStates(allowedOrderStates(order));
|
|
|
|
|
+ context.setAllowedDeliveryStatuses(allowedDeliveryStates(order));
|
|
|
|
|
+
|
|
|
|
|
+ boolean active = order.getState() != 3L && order.getState() != 4L
|
|
|
|
|
+ && order.getPayStatus() != 2L && order.getAfterSaleStatus() == 0L;
|
|
|
|
|
+ context.setCanConfirmOfflinePayment(active && PAY_TYPE_OFFLINE.equals(order.getPayType())
|
|
|
|
|
+ && order.getPayStatus() == 0L);
|
|
|
|
|
+ context.setCanConfirmOfflineRefund(active && PAY_TYPE_OFFLINE.equals(order.getPayType())
|
|
|
|
|
+ && order.getPayStatus() == 1L);
|
|
|
|
|
+
|
|
|
|
|
+ if (PAY_TYPE_OMG.equals(order.getPayType())) {
|
|
|
|
|
+ PosOrderOmgPayment payment = omgPaymentService.getLatestByDdId(String.valueOf(order.getDdId()));
|
|
|
|
|
+ if (payment != null) {
|
|
|
|
|
+ context.setOmgPaymentStatus(payment.getPayStatus());
|
|
|
|
|
+ List<PosOrderOmgRefund> refunds = omgRefundService.listByPayment(payment.getId());
|
|
|
|
|
+ boolean manualDone = refunds.stream().anyMatch(this::isManualRefundDone);
|
|
|
|
|
+ boolean manualPending = !manualDone && refunds.stream().anyMatch(this::isManualRefundPending);
|
|
|
|
|
+ context.setManualRefundPending(manualPending);
|
|
|
|
|
+ context.setRefundUnknown(Integer.valueOf(4).equals(payment.getPayStatus()));
|
|
|
|
|
+ context.setCanReconcileOmg(active && order.getPayStatus() == 0L
|
|
|
|
|
+ && Integer.valueOf(0).equals(payment.getPayStatus()));
|
|
|
|
|
+ context.setCanRefundOmg(active && order.getPayStatus() == 1L
|
|
|
|
|
+ && (Integer.valueOf(1).equals(payment.getPayStatus())
|
|
|
|
|
+ || Integer.valueOf(3).equals(payment.getPayStatus()))
|
|
|
|
|
+ && !manualPending && !manualDone);
|
|
|
|
|
+ context.setCanConfirmManualOmgRefund(active && order.getPayStatus() == 1L
|
|
|
|
|
+ && (manualPending && (Integer.valueOf(1).equals(payment.getPayStatus())
|
|
|
|
|
+ || Integer.valueOf(3).equals(payment.getPayStatus()))
|
|
|
|
|
+ || manualDone && Integer.valueOf(3).equals(payment.getPayStatus())));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return context;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private List<Long> allowedOrderStates(PosOrder order) {
|
|
|
|
|
+ List<Long> allowed = new ArrayList<>();
|
|
|
|
|
+ allowed.add(order.getState());
|
|
|
|
|
+ if (order.getState() == 3L || order.getState() == 4L || order.getPayStatus() == 2L
|
|
|
|
|
+ || order.getAfterSaleStatus() != 0L) {
|
|
|
|
|
+ return allowed;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (order.getState() == 0L) {
|
|
|
|
|
+ if (!PAY_TYPE_OMG.equals(order.getPayType()) || order.getPayStatus() == 1L) {
|
|
|
|
|
+ allowed.add(1L);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (order.getPayStatus() == 0L) {
|
|
|
|
|
+ allowed.add(4L);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (order.getState() == 1L) {
|
|
|
|
|
+ allowed.add(2L);
|
|
|
|
|
+ if (order.getPayStatus() == 0L) {
|
|
|
|
|
+ allowed.add(4L);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (order.getState() == 2L && order.getPayStatus() == 1L
|
|
|
|
|
+ && (order.getType() != 0L || Long.valueOf(3L).equals(order.getDeliveryStatus()))) {
|
|
|
|
|
+ allowed.add(3L);
|
|
|
|
|
+ }
|
|
|
|
|
+ return allowed;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private List<Long> allowedDeliveryStates(PosOrder order) {
|
|
|
|
|
+ List<Long> allowed = new ArrayList<>();
|
|
|
|
|
+ if (order.getType() != 0L) {
|
|
|
|
|
+ return allowed;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (order.getDeliveryStatus() != null) {
|
|
|
|
|
+ allowed.add(order.getDeliveryStatus());
|
|
|
|
|
+ }
|
|
|
|
|
+ if (order.getState() != 2L || order.getPayStatus() == 2L || order.getAfterSaleStatus() != 0L) {
|
|
|
|
|
+ return allowed;
|
|
|
|
|
+ }
|
|
|
|
|
+ Long current = order.getDeliveryStatus();
|
|
|
|
|
+ if (current == null) {
|
|
|
|
|
+ allowed.add(0L);
|
|
|
|
|
+ } else if (current < 2L && order.getQsId() != null) {
|
|
|
|
|
+ allowed.add(current + 1L);
|
|
|
|
|
+ } else if (current == 2L && order.getQsId() != null && order.getPayStatus() == 1L) {
|
|
|
|
|
+ allowed.add(3L);
|
|
|
|
|
+ }
|
|
|
|
|
+ return allowed;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private boolean isManualRefundPending(PosOrderOmgRefund refund) {
|
|
|
|
|
+ return refund.getAction() == null && refund.getRtnCode() == null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private boolean isManualRefundDone(PosOrderOmgRefund refund) {
|
|
|
|
|
+ return refund.getAction() == null && Integer.valueOf(1).equals(refund.getRtnCode());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private PosOrder requireOrder(Long id) {
|
|
|
|
|
+ PosOrder order = id == null ? null : posOrderService.getById(id);
|
|
|
|
|
+ if (order == null) {
|
|
|
|
|
+ throw new ServiceException("订单不存在");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (order.getState() == null || order.getPayStatus() == null || order.getAfterSaleStatus() == null
|
|
|
|
|
+ || order.getType() == null) {
|
|
|
|
|
+ throw new ServiceException("订单状态数据不完整");
|
|
|
|
|
+ }
|
|
|
|
|
+ return order;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void requireReason(AdminOrderActionRequest request) {
|
|
|
|
|
+ if (request == null || request.normalizedReason().isEmpty()
|
|
|
|
|
+ || request.normalizedReason().length() > 200) {
|
|
|
|
|
+ throw new ServiceException("请填写1至200字的调整原因");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private String value(Object value) {
|
|
|
|
|
+ return value == null ? "NULL" : String.valueOf(value);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private String safeName(String name) {
|
|
|
|
|
+ return name == null ? "" : name;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|