From ce08f7866d1bc4ab8c0a2731359b8b0be583a0b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E4=BF=8A=E5=AE=8F?= <389838709@qq.com> Date: Mon, 8 Jul 2024 15:26:18 +0800 Subject: [PATCH] =?UTF-8?q?=E6=89=A3=E6=AC=BE=E5=A4=B1=E8=B4=A5=E5=90=8E?= =?UTF-8?q?=EF=BC=8C=E9=87=8D=E8=AF=95=E6=89=A3=E6=AC=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/cmd/AgreementPaySendSms.php | 2 +- app/cmd/PayOrderRetry.php | 67 ++++++++++++++++++++++++++++++++ app/front/Order.php | 5 ++- app/model/Order.php | 3 ++ app/service/AgreementService.php | 66 +++++++++++++++++++++++++++++++ app/service/BaseService.php | 24 ++++++++---- app/service/CmbService.php | 3 +- app/service/OrderService.php | 2 +- config/console.php | 4 +- 9 files changed, 164 insertions(+), 12 deletions(-) create mode 100644 app/cmd/PayOrderRetry.php diff --git a/app/cmd/AgreementPaySendSms.php b/app/cmd/AgreementPaySendSms.php index b6e0bfb..d21ee16 100644 --- a/app/cmd/AgreementPaySendSms.php +++ b/app/cmd/AgreementPaySendSms.php @@ -65,7 +65,7 @@ class AgreementPaySendSms extends Command // 开始发送扣款短信 $res = AliSms::sendSms(['phone_numbers' => $collection->mobile], 3); $redis->set($cacheKey, true, 'ex', $cacheKeys['ttl']); - if ($res['code'] == "OK") { + if ($res['code'] == 200) { $this->successCount += 1; $output->writeln("短信发送受理成功"); } else { diff --git a/app/cmd/PayOrderRetry.php b/app/cmd/PayOrderRetry.php new file mode 100644 index 0000000..92657fb --- /dev/null +++ b/app/cmd/PayOrderRetry.php @@ -0,0 +1,67 @@ +setName('PayOrderRetry')->setDescription('重试扣款失败的订单'); + + } + + /** + * 所有失败订单每天重试一次,重试到订单创建时间的月底。如果还是失败就进行解约操作 + * 每天11点执行 + * @param Input $input + * @param Output $output + * @return void + */ + protected function execute(Input $input, Output $output) + { + // 本月内的支付失败订单 + // 获取本月的开始时间 + $startOfMonth = new \DateTime('first day of this month 00:00:00'); + $startOfMonthFormatted = $startOfMonth->format('Y-m-d H:i:s'); + + // 获取本月的结束时间 + $endOfMonth = new \DateTime('last day of this month 23:59:59'); + $endOfMonthFormatted = $endOfMonth->format('Y-m-d H:i:s'); + + Order::where(['pay_status' => Order::PAY_STATUS_FAIL])->whereBetweenTime('create_time', strtotime($startOfMonthFormatted), strtotime($endOfMonthFormatted)) + ->where(['is_retry' => Order::RETRY_STATUS_NO]) + ->field('id,order_number,agreement_id,user_id')->chunk(100, function (Collection $orderCollection) use (&$output) { + // 拉取支付失败的订单 + foreach ($orderCollection as $order) { + // 查询签约状态是否取消 + $signInfo = Sign::getByAgreementId($order->agreement_id); + if ($signInfo->isEmpty() || $signInfo->sign_status != Order::STATUS_SIGNED) { + continue; + } + $res = AgreementService::RetryPayOrder($order->order_number, $signInfo->agreement_id); + if ($res['respCode'] == 1000) { + $this->successCount += 1; + $output->writeln("受理成功,等待支付通知"); + } else { + $this->failedCount += 1; + $output->writeln("用户" . $order->user_id . "扣款失败:" . $res['respMsg']); + } + } + }); + Log::info(sprintf("扣款job执行情况:执行成功,累计 %s, 受理成功 %s, 失败 %s", $this->count, $this->successCount, $this->failedCount)); + $output->writeln(sprintf("重试扣款失败的订单执行成功,累计 %s, 受理成功 %s, 失败 %s", $this->count, $this->successCount, $this->failedCount)); + } +} \ No newline at end of file diff --git a/app/front/Order.php b/app/front/Order.php index f2ca03f..62725e5 100644 --- a/app/front/Order.php +++ b/app/front/Order.php @@ -2,7 +2,6 @@ namespace app\front; -use app\config\ResponseCode; use app\service\CmbService; use app\service\OrderService; use app\service\RechargeService; @@ -12,7 +11,11 @@ class Order extends Base { public function list(Request $request): \think\Response { + if (empty($request->user_id)) { + return responseOk(); + } $params['user_id'] = $request->user_id; + $params['is_retry'] = \app\model\Order::RETRY_STATUS_NO; return responseOk(app()->make(OrderService::class)->list($params)); } diff --git a/app/model/Order.php b/app/model/Order.php index f9d7c49..3a7f12e 100644 --- a/app/model/Order.php +++ b/app/model/Order.php @@ -26,6 +26,9 @@ class Order extends BaseModel const REFUND_STATUS_WAIT = 1; const REFUND_STATUS_SUCCESS = 2; const REFUND_STATUS_FAIL = 3; + // 重试状态 + const RETRY_STATUS_NO = 0; // 未重试 + const RETRY_STATUS_YES = 1; // 已重试 const STATUS_TEXT = [ self::STATUS_WAIT_SIGN => '待签约', diff --git a/app/service/AgreementService.php b/app/service/AgreementService.php index a46fab1..6f647d2 100644 --- a/app/service/AgreementService.php +++ b/app/service/AgreementService.php @@ -216,6 +216,68 @@ class AgreementService extends BaseService return CmbHttpUtils::doPost($funcName, $requestParams); } + /** + * 扣款失败,重试 + * + * @param string $orderNumber + * @param string $agreementId + * @return array|mixed|string + */ + public static function RetryPayOrder(string $orderNumber, string $agreementId) + { + // 判断订单状态是否正确 + $order = Order::getByOrderNumber($orderNumber); + if ($order->isEmpty() || $order->pay_status != Order::PAY_STATUS_FAIL || $order->is_retry == Order::RETRY_STATUS_YES) { + return ['respCode' => 1000, 'respMsg' => '处理成功']; + } + + // 处理订单再次扣款 + // 创建新的订单 + $productId = $order->product_id; + $product = Product::getBySupplierProductId($productId); + $newOrder = [ + 'user_id' => $order->user_id, + 'order_number' => StringUtil::makeOrderNumber(), + 'account' => $order->account, + 'type' => $order->type, + 'product_id' => $order->product_id, + 'price' => $order->price, + 'bonus' => $order->bonus, + 'agreement_id' => $order->agreement_id, + 'order_status' => Order::STATUS_SIGNED + ]; + + // 生成重试订单 + Db::startTrans(); + try { + // 创建新订单 + Order::create($newOrder); + // 更新老订单 + $order->is_retry = Order::RETRY_STATUS_YES; + $order->retry_order_num = $newOrder['order_number']; + $res = $order->save(); + if (!$res) { + throw new \Exception("重试支付,更新老订单失败,订单号:" . $orderNumber); + } + Db::commit(); + } catch (\Exception $e) { + Db::rollback(); + Log::error("Job:PayOrderRetry,错误信息:" . $e->getMessage()); + return ['respCode' => 1001, 'respMsg' => $e->getMessage()]; + } + + // 扣款参数 + $requestParams = [ + 'agreementId' => $agreementId, + 'billNo' => $newOrder['order_number'], + 'amount' => $newOrder['price'] * 100, + 'notifyUrl' => config('cmb.pay_notify_url'), + 'productName' => $product['name'] + ]; + $funcName = 'agreementPay'; + return CmbHttpUtils::doPost($funcName, $requestParams); + } + /** * 协议扣款回调处理 * @param array $data @@ -252,6 +314,10 @@ class AgreementService extends BaseService if ($encryptData['result'] == 2) { RechargeService::rechargeOrder($orderNumber); // 支付成功,到直连天下充值 } + // 支付失败 + if ($encryptData['result'] == 3 && BaseService::isLastDayOfMonth()) { + CmbService::releaseMerchant($order->agreement_id); + } if ($res) { return ['respCode' => 1000, 'respMsg' => '处理成功']; } else { diff --git a/app/service/BaseService.php b/app/service/BaseService.php index 1f3277b..15a967e 100644 --- a/app/service/BaseService.php +++ b/app/service/BaseService.php @@ -44,14 +44,14 @@ class BaseService $file = $data['file']; $basePath = config('filesystem.disks.public.root'); validate(['file' => ['fileExt:jpg,png,jpeg,bmp,xlsx,xls,webp|fileSize:20*1024*1024']])->check($data); - $path = '/images/'.date('Y').'/'.date('m').'/'.date('d'); - $fullPath = $basePath.$path; - if(!file_exists($fullPath)) { - mkdir($fullPath,0777,true); + $path = '/images/' . date('Y') . '/' . date('m') . '/' . date('d'); + $fullPath = $basePath . $path; + if (!file_exists($fullPath)) { + mkdir($fullPath, 0777, true); } - $imagePath = Filesystem::putFile($path,$file,'md5'); - $imagePath = str_replace('\\','/',$imagePath); - return ['image_path' =>config('filesystem.disks.public.url').$imagePath ]; + $imagePath = Filesystem::putFile($path, $file, 'md5'); + $imagePath = str_replace('\\', '/', $imagePath); + return ['image_path' => config('filesystem.disks.public.url') . $imagePath]; } public function __call($name, $arguments) @@ -59,4 +59,14 @@ class BaseService //找不到方法,自动调用模型中基类方法 return call_user_func_array([$this->model, $name], $arguments); } + + public static function isLastDayOfMonth() + { + $date = date('Y-m-d'); + // 获取该月的最后一天 + $lastDayOfMonth = date('Y-m-t', strtotime($date)); + + // 比较给定日期和该月的最后一天 + return $date === $lastDayOfMonth; + } } \ No newline at end of file diff --git a/app/service/CmbService.php b/app/service/CmbService.php index af81d8b..dcf178b 100644 --- a/app/service/CmbService.php +++ b/app/service/CmbService.php @@ -167,7 +167,8 @@ class CmbService extends BaseService if ($res['result'] == 2) { // 扣款成功 RechargeService::rechargeOrder($orderNumber); } - if ($res['result'] == 3) { // 扣款失败 解约 + // 月底不进行重试,失败直接解约 + if ($res['result'] == 3 && BaseService::isLastDayOfMonth()) { // 扣款失败 解约 self::releaseMerchant($order->agreement_id); } return true; diff --git a/app/service/OrderService.php b/app/service/OrderService.php index 4305436..a6e6625 100644 --- a/app/service/OrderService.php +++ b/app/service/OrderService.php @@ -25,7 +25,7 @@ class OrderService extends BaseService public function list($params) { - return $this->model->searchPages(['order_number', 'type', 'status', 'pay_status', 'create_at', 'user_id'], $params)->toArray(); + return $this->model->searchPages(['order_number', 'type', 'status', 'create_at', 'user_id', 'is_retry'], $params)->toArray(); } /** diff --git a/config/console.php b/config/console.php index 35484b1..a5a7c07 100644 --- a/config/console.php +++ b/config/console.php @@ -10,6 +10,8 @@ return [ 'getPayOrder' => \app\cmd\GetPayOrder::class, 'getRefundOrder' => \app\cmd\getRefundOrder::class, 'queryAgreeStatus' => \app\cmd\QueryAgreeStatus::class, - 'queryRechargeOrder' =>\app\cmd\QueryRechargeOrder::class + 'queryRechargeOrder' =>\app\cmd\QueryRechargeOrder::class, + 'payOrderRetry' =>\app\cmd\PayOrderRetry::class, + 'agreementPaySendSms' =>\app\cmd\AgreementPaySendSms::class ], ];