checkLevelFillOnceMinAndMax($levelService, $goodsGoodsNumList, $isBuy)) { $this->setError($cartService->getError()); return false; } // 验证商品库存 if (!$goodsService->checkGoodsStockByGoodsIds($goodsGoodsNumList)) { $this->setError($goodsService->getError()); return false; } // 验证商品数据 if (!$goodsService->checkGoodsDataByGoodsIds(array_column($goodsGoodsNumList, 'goods_id'))) { $this->setError($goodsService->getError()); return false; } $configs = \Yii::$app->services->setting->addons->get($this->mall_id, AddonsEnum::ADDONS_NAME_CLOUD_STOCK, 'basic'); $isEnableOrderNumLimit = $configs['is_enable_order_num_limit'] ?? 0; $orderLimitType = $configs['order_limit_type'] ?? 2; $orderLimitNum = $configs['order_limit_num'] ?? 0; if (!$isEnableOrderNumLimit) { return true; } switch ($orderLimitType) { case 1: // 限制kg if ($cartService->getCartWeight($goodsGoodsNumList) > $orderLimitNum) { $this->setError(sprintf('超出最大可自提重量, 最大可自提重量为:%.2fkg', $orderLimitNum)); return false; } break; case 2: // 限制件数 if ($cartService->getCartNum() > $orderLimitNum) { $this->setError(sprintf('超出最大可自提件数, 最大可自提:%d件', $orderLimitNum)); return false; } break; } return true; } /** * 获取计算运费from * * @param CloudStockAgentGoodsService $goodsService * @param int $mallId * @param int $userId * @param int $addrId * @param array $goodsAndNumList * * @return mixed */ public function getForm( CloudStockAgentGoodsService $goodsService, int $mallId, int $userId, int $addrId, array $goodsAndNumList ) { // 计算运费 $orderFormLogic = new OrderFormLogic(); $form = $orderFormLogic->getDefaultform($mallId, $userId); $freightAttr = []; $goodsIds = array_column($goodsAndNumList, 'goods_id'); $goodsAttrMap = $goodsService->getGoodsAttrMap($goodsIds); foreach ($goodsAndNumList as $orderDetail) { if (!isset($goodsAttrMap[$orderDetail['goods_id']])) { throw new \RuntimeException(sprintf('商品异常,ID:%d', $orderDetail['goods_id'])); } $freightAttr[] = ['attr_id' => $goodsAttrMap[$orderDetail['goods_id']], 'num' => $orderDetail['num']]; } if (empty($freightAttr)) { throw new \RuntimeException('当前订单不允许修改地址'); } $form->attributes = [ 'type' => PreviewTypeEnum::FREIGHT, 'data' => [ 'shipping_type' => \common\enums\ShippingTypeEnum::LOGISTICS, 'freight_attr' => $freightAttr, 'address_id' => $addrId ] ]; $form['action'] = AppEnum::ACTION_CREATE; // 初始化订单数据 $orderInitDataLogic = new OrderInitDataLogic(); $form = $orderInitDataLogic->execute($form, $form->type); if ($form === false) throw new \RuntimeException($orderInitDataLogic->getError()); // 购买限制验证 $orderLimitLogic = new OrderLimitLogic(); $form = $orderLimitLogic->run($form); if ($form === false) throw new RuntimeException($orderLimitLogic->getError()); // 营销减免活动 $orderMarketingLogic = new OrderMarketingLogic(); $form = $orderMarketingLogic->run($form, true); if ($form === false) throw new \RuntimeException($orderMarketingLogic->getError()); if (empty($form['group_order_goods']) || !is_array($form['group_order_goods'])) { throw new \RuntimeException('订单异常'); } if (empty($form['group_order_goods'])) { throw new \RuntimeException('订单异常'); } return $form; } /** * 得到运费 * * @param CloudStockAgentGoodsService $goodsService * @param int $mall_id * @param int $user_id * @param int $addr_id * @param array $goodsAndNumListData * @param object|null $form * * @return float */ public function getFreight( CloudStockAgentGoodsService $goodsService, int $mall_id, int $user_id, int $addr_id, array $goodsAndNumListData, object $form = null ): float { if (is_null($form)) { $form = $this->getForm($goodsService, $mall_id, $user_id, $addr_id, $goodsAndNumListData); } if (!isset($form['marketing']['deduct']['shipping_fee']['money'])) { throw new \RuntimeException('订单异常'); } return $form['marketing']['deduct']['shipping_fee']['money']; } /** * @param CloudStockAgentOrder $order * * @return bool * @throws Exception */ public function shippingRefunds(CloudStockAgentOrder $order): bool { // 不需要退 if ($order->pay_money <= 0 || $order->is_shipping_refunded || empty($order->trade_no)) { return true; } // 已退款或退款中 直接返回 if (!$this->isAllowApplyRefund($order->id)) { return true; } // 查出流水表 $paymentTrade = PaymentTrade::findOne([ 'out_trade_no' => $order->trade_no, 'mall_id' => $this->mall_id, 'is_pay' => StatusEnum::ENABLED ]); if (empty($paymentTrade)) { $this->setError('未查询到支付记录'); return false; } if (empty($paymentTrade->pay_fee)) { return true; } $baseLogData = [ 'method' => __METHOD__, 'agent_order_id' => $order->id ]; // 创建退款订单 try { [ 'cloudStockAgentOrderRefund' => $cloudStockAgentOrderRefund, 'mallOrder' => $mallOrder, 'mallOrderRefund' => $mallOrderRefund ] = $this->createOrderRefund($paymentTrade, $order); } catch (Exception $e) { Yii::$app->custom->logs(FormatHelper::exception($e, '创建售后订单失败'), $baseLogData); $this->setError('退款失败,请稍后重试!'); return false; } // 执行退款 $defaultErrMsg = '自提订单退款失败'; try { $refundRes = $this->refund($paymentTrade, $order, $cloudStockAgentOrderRefund, $mallOrderRefund); } catch (Exception $e) { $refundRes = false; $refundErrorMsg = FormatHelper::exception($e, $defaultErrMsg); } // 保存打款结果 if (!$this->saveRefundRes($order, $cloudStockAgentOrderRefund, $refundRes)) { Yii::$app->custom->logs( '取消自提修改打款状态失败', array_merge( $baseLogData, ['agent_order_refund_id' => $cloudStockAgentOrderRefund->id, 'refundRes' => $refundRes] ) ); $this->setError($refundRes ? '打款成功,修改打款状态失败!' : '退款失败,请稍后重试!'); return false; } if (!$refundRes) { Yii::$app->custom->logs( $refundErrorMsg ?? $defaultErrMsg, array_merge( $baseLogData, ['agent_order_refund_id' => $cloudStockAgentOrderRefund->id, 'refundRes' => false] ) ); $this->setError('退款失败,请稍后重试!'); return false; } return true; } /** * 是否可以申请退款 * * @param int $orderId * * @return bool */ public function isAllowApplyRefund(int $orderId): bool { $refund = CloudStockAgentOrderRefund::find() ->where([ 'order_id' => $orderId, 'order_type' => PaymentTradeItemRefundEnum::ORDER_TYPE_AGENT_ORDER, 'status' => StatusEnum::ENABLED, 'mall_id' => $this->mall_id ]) ->select('is_refund') ->asArray() ->one(); return empty($refund) || $refund['is_refund'] == StatusEnum::DELETE; } /** * 保存打款结果 * * @param CloudStockAgentOrder $order * @param CloudStockAgentOrderRefund $refund * @param bool $refundRes 结果 * * @return bool */ public function saveRefundRes(CloudStockAgentOrder $order, CloudStockAgentOrderRefund $refund, bool $refundRes): bool { // 保存打款结果 if ($refundRes) { $refund->is_refund = StatusEnum::ENABLED; $refund->refund_at = time(); $order->is_shipping_refunded = StatusEnum::ENABLED; } else { $refund->is_refund = StatusEnum::DELETE; } return $refund->save() && $order->save(); } /** * 创建售后数据 * * @param PaymentTrade $paymentTrade * @param CloudStockAgentOrder $order * * @return array * @throws Exception */ public function createOrderRefund(PaymentTrade $paymentTrade, CloudStockAgentOrder $order): array { $t = Yii::$app->db->beginTransaction(); try { // 创建退款表 $cloudStockAgentOrderRefund = CloudStockAgentOrderRefund::setData([ 'mall_id' => $this->mall_id, 'order_id' => $order->id, 'order_type' => PaymentTradeItemRefundEnum::ORDER_TYPE_AGENT_ORDER, 'user_id' => $order->user_id, 'order_no' => Order::generateOrderNo('csaor'), 'refund_price' => $paymentTrade->pay_fee, 'reality_refund_price' => $paymentTrade->pay_fee, ]); if (empty($cloudStockAgentOrderRefund->id)) { throw new RuntimeException('生成退款单号失败'); } $mallOrder = new Order(); $mallOrder->mall_id = $this->mall_id; $mallOrderRefund = new OrderRefund(); $mallOrderRefund->mall_id = $this->mall_id; $mallOrderRefund->id = $cloudStockAgentOrderRefund->id; $mallOrderRefund->order_id = $cloudStockAgentOrderRefund->order_id; $mallOrderRefund->order_no = $cloudStockAgentOrderRefund->order_no; $mallOrderRefund->refund_price = $cloudStockAgentOrderRefund->refund_price; $mallOrderRefund->reality_refund_price = $cloudStockAgentOrderRefund->reality_refund_price; $paymentTrade->addRefundItem($mallOrderRefund, $mallOrder, CloudStockEnum::ADDONS_NAME); $t->commit(); return compact('cloudStockAgentOrderRefund', 'mallOrder', 'mallOrderRefund'); } catch (Exception $e) { $t->rollBack(); throw $e; } } /** * 退款 * * @param PaymentTrade $paymentTrade * @param CloudStockAgentOrder $order * @param CloudStockAgentOrderRefund $refund * * @return bool * @throws Exception */ public function refund(PaymentTrade $paymentTrade, CloudStockAgentOrder $order, CloudStockAgentOrderRefund $refund, OrderRefund $mallOrderRefund): bool { $params = [ 'refund_price' => $refund->reality_refund_price, ]; $list = $paymentTrade->getRefundFeeList($refund->id, CloudStockEnum::ADDONS_NAME); foreach ($list as $item) { if (in_array($item->pay_type, [PayTypeEnum::BALANCE, PayTypeEnum::SCORE, PayTypeEnum::JOINPAY, PayTypeEnum::ALI, PayTypeEnum::JOINPAY_FAST_PAY, PayTypeEnum::WECHAT, PayTypeEnum::SANDPAY_FAST_PAY])) { $refund_price = $item->getRefundFee($params, $refund); // 退款金额需要大于0 组合支付金额过小的情况下 分配子订单退款金额可能为0 if ($refund_price > 0) { //原路返回 $res = Yii::$app->services->pay->refund( $refund->refund_price, $refund_price, $paymentTrade->out_trade_no, $order, '订单取消自提退款', CloudStockEnum::ADDONS_NAME, 'cloud_stock_cancel_pickup_refund', $item->pay_type ); if ($res === false) { throw new RuntimeException('退款失败'); } } } elseif ( // 当res返回为false 说明没有相对于的退款方式 $res = GlobalInvoke::exec(GlobalInvoke::INVOKE_ORDER_REFUND, $this->mall_id, [ 'id' => $refund->id, 'mall_id' => $refund->mall_id, 'refund' => $mallOrderRefund, 'params' => $params, 'order_no' => $order['order_no'], 'refund_order_no' => $refund['order_no'], 'payment_trade_id' => $item->getTradeId(), 'refund_price' => $item->getRefundFee($params, $refund), 'refund_source' => CloudStockEnum::ADDONS_NAME, 'pay_type' => $item->pay_type, ]) ) { // 当res不为空 进此判断 list($boolean, $msg) = $res; if ($boolean === false) { $this->setError($msg); return false; } } } // 设置为已退款 $paymentTrade->setRefund($refund->id, CloudStockEnum::ADDONS_NAME); return true; } /** * 修改备注 * * @param int $id * @param string $remark * @param bool $isSeller 是否是卖家备注 * @param callable|null $callback 数据验证等 * * @return bool */ public function modifyRemark(int $id, string $remark, bool $isSeller = false, callable $callback = null): bool { $order = CloudStockAgentOrder::findOne([ 'id' => $id, 'mall_id' => $this->mall_id, 'status' => StatusEnum::ENABLED ]); if (empty($order)) { $this->setError('订单不存在'); return false; } if ($callback !== null) { try { $callback($order); } catch (Exception $e) { $this->setError($e->getMessage()); return false; } } if ($isSeller) { $order->seller_remark = $remark; } else { $order->remark = $remark; } return $order->save(); } /** * 修改收货地址 * * @param array $params * @param bool $isCheckOnlyOnce 是否需要验证只允许修改一次 * @param callable|null $callback 自定义验证函数 * * @return bool */ public function modifyAddress(array $params, bool $isCheckOnlyOnce = false, callable $callback = null): bool { $order = CloudStockAgentOrder::find() ->where([ 'mall_id' => $this->mall_id, 'id' => $params['id'], 'send_status' => [CloudStockEnum::PICK_ORDER_STATUS0, CloudStockEnum::PICK_ORDER_STATUS3] ]) ->with(['details']) ->one(); if (empty($order)) { $this->setError('当前订单不允许修改收货地址'); return false; } if ($callback !== null) { try { $callback($order, $params); } catch (Exception $e) { $this->setError($e->getMessage()); return false; } } // 验证只允许修改一次地址 if ($isCheckOnlyOnce && $order->is_delivery_address_modified) { $this->setError('当前订单已修改过收货地址'); return false; } $params['is_delivery_address_modified'] = 1; if (!$order->load($params, '') || !$order->save()) { Yii::$app->custom->logs( '修改自提订单收货地址失败', ['order' => $order->toArray(), 'err_msg' => $order->getErrorMessage()] ); $this->setError('修改地址失败'); return false; } return true; } /** * 验证新地址的运费 * * @param CloudStockAgentOrder $order * @param int $addressId * * @return array */ public function checkNewAddressFreight(CloudStockAgentOrder $order, int $addressId): array { $goodsService = new CloudStockAgentGoodsService(['mall_id' => $this->mall_id]); $form = $this->getForm($goodsService, $this->mall_id, $order->user_id, $addressId, $order['details']); if (!empty(\Yii::$app->services->setting->addons->get($this->mall_id, AddonsEnum::ADDONS_NAME_CLOUD_STOCK, 'basic.is_enable_freight_charges'))) { if (empty($order['details'])) { throw new RuntimeException('当前订单不允许修改收货地址'); } try { if (bccomp($this->getFreight($goodsService, $this->mall_id, $order->user_id, $addressId, $order['details'], $form), $order->pay_money, 2) > 0) { throw new RuntimeException('当前地址运费超过原地址运费,不允许修改'); } } catch (Exception $e) { throw new RuntimeException($e instanceof RuntimeException ? $e->getMessage() : '修改收货地址失败,请稍后重试!'); } } $groupOrderGoods = reset($form['group_order_goods'])['mch_order_info'] ?? []; if (empty($groupOrderGoods)) { throw new RuntimeException('修改收货地址失败,请稍后重试!'); } return [ 'name' => $groupOrderGoods['receiver_name'], 'mobile' => $groupOrderGoods['mobile'], 'address' => str_replace(' ', '', $groupOrderGoods['region_name']) . $groupOrderGoods['address'], 'province_id' => $groupOrderGoods['province'], 'city_id' => $groupOrderGoods['city'], 'district_id' => $groupOrderGoods['area'], ]; } }