Kaynağa Gözat

feat(lakala): 在 WechatOrderCron 中新增定时任务 orderExpire,用于处理超时未支付订单
- 支持根据商户编号和终端编号查询交易状态
- 新增 closeOrder 方法用于关闭超时订单并更新支付状态
- 优化查询条件,增加 biz_type 和 expire_time 过滤
- 调整代码格式,增强可读性
- 更新测试用例,增加 orderExpire 测试方法
- 修复部分参数传递和默认值逻辑问题

runphp 3 ay önce
ebeveyn
işleme
d725346d28

+ 24 - 3
src/Cron/WechatOrderCron.php

@@ -1,5 +1,6 @@
 <?php
 declare(strict_types=1);
+
 namespace SixShop\Lakala\Cron;
 
 use SixShop\Core\Attribute\Cron;
@@ -13,7 +14,7 @@ class WechatOrderCron
 {
     public function __construct(
         private PaymentRecordService $paymentRecordService,
-        private ExpressService $expressService,
+        private ExpressService       $expressService,
     )
     {
         $this->expressService->getAllDelivery();
@@ -27,19 +28,39 @@ class WechatOrderCron
             ->leftJoin('lakala_wechat_payment w', 'p.id = w.payment_id')
             ->where([
                 'p.pay_type' => 'lakala',
+                'p.biz_type' => 1,
                 'p.status' => 2,
                 'o.shipping_status' => 1
             ])
             ->where(function (Query $query) {
-                $query->whereIn('w.order_state', [1,2])
+                $query->whereIn('w.order_state', [1, 2])
                     ->whereOr('w.order_state', null);
             })
             ->field('p.id,w.order_state')
-            ->select()->each(function ($payment){
+            ->select()->each(function ($payment) {
                 if ($payment->order_state === null) {
                     $this->paymentRecordService->createWechatOrder($payment->id);
                 }
                 $this->paymentRecordService->updateWechatOrder($payment->id);
             });
     }
+
+    #[Cron('0 */20 * * * *', 'lakala.expire')]
+    public function orderExpire(): void
+    {
+        // 订单支付超时关闭订单
+        ExtensionPaymentModel::alias('p')
+            ->leftJoin('order o', 'o.id = p.order_id')
+            ->where([
+                'p.pay_type' => 'lakala',
+                'p.biz_type' => 1,
+                'p.status' => 0,
+                ['p.expire_time', '<', time()],
+                'o.order_status' => 10,
+            ])
+            ->field('p.id,p.order_id,o.delete_time')
+            ->select()->each(function ($payment) {
+                $this->paymentRecordService->closeOrder($payment->id);
+            });
+    }
 }

+ 25 - 1
src/Service/PaymentRecordService.php

@@ -8,16 +8,21 @@ use app\model\OrderGoods;
 use SixShop\Core\Exception\LogicException;
 use SixShop\Lakala\Enum\WechatOrderStateEnum;
 use SixShop\Lakala\Model\LakalaWechatPaymentModel;
+use SixShop\Payment\Enum\PaymentStatusEnum;
 use SixShop\Payment\Model\ExtensionPaymentModel;
 use SixShop\Wechat\Entity\WechatDeliveryEntity;
 use SixShop\Wechat\Service\ExpressService;
 use think\db\Query;
+use think\facade\Db;
 use think\facade\Log;
 use think\Paginator;
 
 class PaymentRecordService
 {
-    public function __construct(private ExpressService $expressService)
+    public function __construct(
+        private ExpressService $expressService,
+        private TransactionService $transactionService,
+    )
     {
     }
 
@@ -165,4 +170,23 @@ class PaymentRecordService
             }
         }
     }
+
+    public function closeOrder(int $paymentID):void
+    {
+        $payment = ExtensionPaymentModel::find($paymentID);
+        $resData = $this->transactionService->queryTrade(
+            tradeNo: $payment->transaction_id,
+            merchantNo: $payment->payment_param['merchant_no'],
+        );
+        if ($resData->trade_state == 'CLOSE') {
+            $payment->status = PaymentStatusEnum::CLOSED;
+            $payment->status_desc = $resData->trade_state_desc;
+            $payment->payment_result = $resData;
+            $order = Order::findOrEmpty($payment->order_id);
+            Db::transaction(function () use ($payment,$order) {
+               $payment->save();
+               $order->isEmpty() || $order->delete();
+            });
+        }
+    }
 }

+ 43 - 25
src/Service/TransactionService.php

@@ -1,5 +1,6 @@
 <?php
 declare(strict_types=1);
+
 namespace SixShop\Lakala\Service;
 
 use SixShop\Lakala\Log;
@@ -28,6 +29,7 @@ class TransactionService
     private QueryTradequeryApi $queryTradequeryApi;
 
     private LakalaApi $lakalaApi;
+
     public function __construct(private Config $config, private Log $log)
     {
         $this->transPreorderApi = new TransPreorderApi($config->getV3Config());
@@ -52,18 +54,18 @@ class TransactionService
      * @link https://o.lakala.com/#/home/document/detail?id=110
      */
     public function preOrder(
-        string $outTradeNo,
-        float $totalAmount,
-        LocationInfo $locationInfo,
-        string $accountType = 'WECHAT',
-        string $transType = '71',
-        string $busiMode = 'ACQ',
-        string $subject = '',
-        string $payOrderNo = '',
-        string $settleType = '',
-        string $remark = '',
+        string             $outTradeNo,
+        float              $totalAmount,
+        LocationInfo       $locationInfo,
+        string             $accountType = 'WECHAT',
+        string             $transType = '71',
+        string             $busiMode = 'ACQ',
+        string             $subject = '',
+        string             $payOrderNo = '',
+        string             $settleType = '',
+        string             $remark = '',
         TradeAccBusiFields $accBusiFields = null,
-    ):object
+    ): object
     {
         $request = new TransPreorderRequest();
 
@@ -85,14 +87,14 @@ class TransactionService
         $request->setRemark($remark);
         $request->setAccBusiFields($accBusiFields);
         $request->setCompleteNotifyUrl($this->config->complete_notify_url);
-        $this->log->debug('preOrder request:'.json_encode($request));
+        $this->log->debug('preOrder request:' . json_encode($request));
         $response = $this->transPreorderApi->transPreorder($request);
-        $this->log->debug('preOrder response:'.json_encode($response->getRespData()));
+        $this->log->debug('preOrder response:' . json_encode($response->getRespData()));
         if ($response->getCode() == 'BBS00000') {
             return $response->getRespData();
         } else {
             throw_logic_exception(
-                msg:$response->getMsg(),
+                msg: $response->getMsg(),
                 status: $response->getCode(),
                 data: $response->getRespData(),
             );
@@ -106,12 +108,28 @@ class TransactionService
      *
      * @param string $tradeNo 拉卡拉订单系统订单号
      * @param string $outTradeNo 商户订单号
+     * @param string $merchantNo 商户编号
+     * @param string $termNo 终端编号
+     *
+     * @link https://o.lakala.com/#/home/document/detail?id=116
      */
-    public function queryTrade(string $tradeNo = '', string $outTradeNo = ''):object
+    public function queryTrade(string $tradeNo = '', string $outTradeNo = '', string $merchantNo = '', string $termNo = ''): object
     {
         $request = new QueryTradequeryRequest();
-        $request->setMerchantNo($this->config->merchant_no);
-        $request->setTermNo($this->config->term_no);
+        if ($merchantNo === '') {
+            $merchantNo = $this->config->merchant_no;
+            $termNo = $this->config->term_no;
+        }
+        $request->setMerchantNo($merchantNo);
+        if ($merchantNo && $termNo === '') {
+            foreach ($this->config->sub_merchant_list as $subMerchant) {
+                if ($subMerchant['merchant_no'] == $merchantNo) {
+                    $termNo = $subMerchant['term_no'];
+                    break;
+                }
+            }
+        }
+        $request->setTermNo($termNo ?: $this->config->term_no);
         $request->setTradeNo($tradeNo);
         $request->setOutTradeNo($outTradeNo);
         $response = $this->queryTradequeryApi->queryTradequery($request);
@@ -120,7 +138,7 @@ class TransactionService
             return $response->getRespData();
         } else {
             throw_logic_exception(
-                msg:$response->getMsg(),
+                msg: $response->getMsg(),
                 status: $response->getCode(),
                 data: $response->getRespData(),
             );
@@ -139,12 +157,12 @@ class TransactionService
      * @link https://o.lakala.com/#/home/document/detail?id=892
      */
     public function refund(
-        string $refundTradeNo,
-        float $refundAmount,
+        string       $refundTradeNo,
+        float        $refundAmount,
         LocationInfo $locationInfo,
-        string $originTradeNo = '',
-        string $originOutTradeNo = '',
-        string $refundReason = '',
+        string       $originTradeNo = '',
+        string       $originOutTradeNo = '',
+        string       $refundReason = '',
     )
     {
         $request = new ModelRequest();
@@ -152,7 +170,7 @@ class TransactionService
             'merchant_no' => $this->config->merchant_no,
             'term_no' => $this->config->term_no,
             'out_trade_no' => $refundTradeNo,
-            'refund_amount' => round($refundAmount*100),
+            'refund_amount' => round($refundAmount * 100),
             'refund_reason' => $refundReason,
             'origin_out_trade_no' => $originOutTradeNo,
             'origin_trade_no' => $originTradeNo,
@@ -166,7 +184,7 @@ class TransactionService
             return $response->getRespData();
         } else {
             throw_logic_exception(
-                msg:$response->getMsg(),
+                msg: $response->getMsg(),
                 status: $response->getCode(),
                 data: $response->getRespData(),
             );

+ 9 - 3
tests/Cron/WechatOrderCronTest.php

@@ -11,15 +11,21 @@ class WechatOrderCronTest extends TestCase
     private WechatOrderCron $wechatOrderCron;
     protected function setUp(): void
     {
+        Db::listen(function ($sql, $time, $explain) {
+            dump($sql);
+        });
         $this->wechatOrderCron = app(WechatOrderCron::class);
     }
 
     #[Test]
     public function orderShipping()
     {
-        Db::listen(function ($sql, $time, $explain) {
-            dump($sql);
-        });
         $this->wechatOrderCron->orderShipping();
     }
+
+    #[Test]
+    public function orderExpire()
+    {
+        $this->wechatOrderCron->orderExpire();
+    }
 }

+ 1 - 2
tests/Service/TransactionServiceTest.php

@@ -55,8 +55,7 @@ class TransactionServiceTest extends TestCase
     public function queryTrade()
     {
         $response = $this->transactionService->queryTrade(
-        // tradeNo: '20251119110113130266202252424725',
-            outTradeNo: '20251119145747020294',
+         tradeNo: '20251201110113130266241823831000', outTradeNo: '20251119161738023242',
         );
         dump($response);
     }