| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587 |
- <?php
- namespace addons\CloudStock\common\services;
- use addons\CloudStock\common\enums\CloudStockEnum;
- use addons\CloudStock\common\enums\PaymentTradeItemRefundEnum;
- use addons\CloudStock\common\logic\order\OrderLimitLogic;
- use addons\CloudStock\common\models\CloudStockAgentOrder;
- use addons\CloudStock\common\models\CloudStockAgentOrderRefund;
- use common\enums\AddonsEnum;
- use common\enums\AppEnum;
- use common\enums\PayTypeEnum;
- use common\enums\PreviewTypeEnum;
- use common\enums\StatusEnum;
- use common\globals\GlobalInvoke;
- use common\helpers\FormatHelper;
- use common\logic\order\OrderFormLogic;
- use common\logic\order\OrderInitDataLogic;
- use common\logic\order\OrderMarketingLogic;
- use common\models\common\PaymentTrade;
- use common\models\goods\GoodsAttr;
- use common\models\order\Order;
- use common\models\order\OrderRefund;
- use Exception;
- use RuntimeException;
- use Yii;
- class CloudStockAgentOrderService extends BaseService
- {
- /**
- * 验证商品
- *
- * @param CloudStockAgentCartService $cartService
- * @param CloudStockLevelService $levelService
- * @param CloudStockAgentGoodsService $goodsService
- * @param array $goodsGoodsNumList
- * @param bool $isBuy
- *
- * @return bool
- */
- public function checkLimit(
- CloudStockAgentCartService $cartService,
- CloudStockLevelService $levelService,
- CloudStockAgentGoodsService $goodsService,
- array $goodsGoodsNumList = [],
- bool $isBuy = true
- ): bool
- {
- // 验证代理权重允许最大和最少提货的数量
- if (!$cartService->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'],
- ];
- }
- }
|