diff --git a/addons/shopro_wechat_trade/README.md b/addons/shopro_wechat_trade/README.md index f07b99e..b6f0b14 100644 --- a/addons/shopro_wechat_trade/README.md +++ b/addons/shopro_wechat_trade/README.md @@ -55,6 +55,11 @@ php think shopro_wechat_trade:verify php think shopro_wechat_trade:init-path php think shopro_wechat_trade:backfill php think shopro_wechat_trade:retry-failed + +# 指定订单:商城确认收货 + 微信 confirm(截图里分账 FREEZE 的单可批量处理) +php think shopro_wechat_trade:confirm-orders --sn=202608500438103091004500,202606584688620091002100 +# 商城已是「已收货」、只补微信: +php think shopro_wechat_trade:confirm-orders --sn=订单号 --skip-mall ``` ## 触发点 diff --git a/addons/shopro_wechat_trade/command/ConfirmOrders.php b/addons/shopro_wechat_trade/command/ConfirmOrders.php new file mode 100644 index 0000000..4e77a2f --- /dev/null +++ b/addons/shopro_wechat_trade/command/ConfirmOrders.php @@ -0,0 +1,191 @@ +setName('shopro_wechat_trade:confirm-orders') + ->addOption('sn', null, Option::VALUE_REQUIRED, '订单号,多个用英文逗号分隔') + ->addOption('id', null, Option::VALUE_REQUIRED, '订单 id,多个用英文逗号分隔') + ->addOption('skip-mall', null, Option::VALUE_NONE, '跳过商城 getedItem,仅调微信 confirm(商城已全部收货时用)') + ->addOption('skip-wechat', null, Option::VALUE_NONE, '仅商城确认收货,不同步微信') + ->addOption('dry-run', null, Option::VALUE_NONE, '只打印将执行的操作') + ->setDescription('Confirm orders in mall and sync WeChat notify_confirm_receive'); + } + + protected function execute(Input $input, Output $output) + { + if (!Bridge::isEnabled()) { + $output->writeln('[FAIL] shopro_wechat_trade 未启用或 wechat_trade_enable=0'); + return 1; + } + + $orderIds = $this->resolveOrderIds($input); + if (!$orderIds) { + $output->writeln('请指定 --sn=订单号 或 --id=订单id'); + return 1; + } + + $skipMall = (bool) $input->getOption('skip-mall'); + $skipWechat = (bool) $input->getOption('skip-wechat'); + $dryRun = (bool) $input->getOption('dry-run'); + + $service = new WxShippingService(); + $orderModel = new Order(); + $ok = 0; + $fail = 0; + + foreach ($orderIds as $orderId) { + $order = Order::with('item')->where('id', $orderId)->find(); + if (!$order) { + $output->writeln("[SKIP] 订单不存在 id={$orderId}"); + $fail++; + continue; + } + $output->writeln('--- order#' . $order['id'] . ' ' . ($order['order_sn'] ?? '') . ' ---'); + + if (!Bridge::shouldHandleOrder($order)) { + $output->writeln('[SKIP] 非小程序 wechat/huifu_wechat 或缺少 transaction_id'); + $fail++; + continue; + } + + if (!$skipMall) { + try { + $this->mallConfirmItems($orderModel, $order, $dryRun, $output); + } catch (\Exception $e) { + $output->writeln(' [FAIL] 商城确认: ' . $e->getMessage()); + $fail++; + continue; + } + } else { + $output->writeln(' [info] skip-mall:不修改商城收货状态'); + } + + if ($skipWechat) { + $output->writeln(' [info] skip-wechat:跳过微信同步'); + $ok++; + continue; + } + + if (!SyncLogger::hasSuccessfulShipping($orderId)) { + $output->writeln(' [WARN] 无 shipping success,先尝试 upload_shipping_info'); + if ($dryRun) { + $output->writeln(' [dry-run] syncOrderShipping'); + } else { + try { + $service->syncOrderShipping($orderId); + $output->writeln(' [OK] shipping 同步成功'); + } catch (\Exception $e) { + $output->writeln(' [FAIL] shipping: ' . $e->getMessage()); + $fail++; + continue; + } + } + } + + if (SyncLogger::hasSuccessfulConfirm($orderId)) { + $output->writeln(' [OK] 微信 confirm 已是 success,跳过'); + $ok++; + continue; + } + + if ($dryRun) { + $output->writeln(' [dry-run] syncOrderConfirm'); + $ok++; + continue; + } + + try { + $service->syncOrderConfirm($orderId); + $output->writeln(' [OK] 微信 confirm 同步成功'); + $ok++; + } catch (\Exception $e) { + $output->writeln(' [FAIL] confirm: ' . $e->getMessage()); + $fail++; + } + } + + $output->writeln(''); + $output->writeln("完成:成功 {$ok},失败 {$fail}"); + if (!$skipWechat && !$dryRun && $ok > 0) { + $output->writeln('汇付分账(非 FREEZE 后):php think shopro_huifu:split-retry'); + } + return $fail > 0 ? 1 : 0; + } + + protected function resolveOrderIds(Input $input) + { + $ids = []; + $snOpt = trim((string) $input->getOption('sn')); + if ($snOpt !== '') { + foreach (preg_split('/[\s,,]+/', $snOpt) as $sn) { + $sn = trim($sn); + if ($sn === '') { + continue; + } + $row = Db::name('shopro_order')->where('order_sn', $sn)->find(); + if (!$row && strlen($sn) < 24) { + $row = Db::name('shopro_order')->where('order_sn', 'like', $sn . '%')->order('id', 'desc')->find(); + } + if ($row) { + $ids[] = intval($row['id']); + } + } + } + $idOpt = trim((string) $input->getOption('id')); + if ($idOpt !== '') { + foreach (preg_split('/[\s,,]+/', $idOpt) as $id) { + $id = intval($id); + if ($id > 0) { + $ids[] = $id; + } + } + } + return array_values(array_unique($ids)); + } + + protected function mallConfirmItems(Order $orderModel, $order, $dryRun, Output $output) + { + $items = $order->item ?? []; + foreach ($items as $item) { + $itemArr = is_object($item) && method_exists($item, 'toArray') ? $item->toArray() : (array) $item; + $status = intval($itemArr['dispatch_status'] ?? 0); + if ($status >= OrderItem::DISPATCH_STATUS_GETED) { + continue; + } + if ($status < OrderItem::DISPATCH_STATUS_SENDED) { + $output->writeln(' [FAIL] item#' . ($itemArr['id'] ?? '') . ' 未发货,请先发货再确认'); + throw new \RuntimeException('存在未发货商品'); + } + if (in_array(intval($itemArr['refund_status'] ?? 0), [OrderItem::REFUND_STATUS_OK, OrderItem::REFUND_STATUS_FINISH], true)) { + $output->writeln(' [SKIP] item#' . ($itemArr['id'] ?? '') . ' 已退款'); + continue; + } + if ($dryRun) { + $output->writeln(' [dry-run] getedItem item#' . ($itemArr['id'] ?? '')); + continue; + } + Db::transaction(function () use ($orderModel, $order, $item) { + $orderModel->getedItem($order, $item, ['oper_type' => 'admin']); + }); + $output->writeln(' [OK] 商城确认 item#' . ($itemArr['id'] ?? '')); + } + } +} diff --git a/application/command.php b/application/command.php index ea61c8a..ca02e81 100644 --- a/application/command.php +++ b/application/command.php @@ -33,6 +33,7 @@ return [ 'addons\shopro_wechat_trade\command\ProbeOrderPath', 'addons\shopro_wechat_trade\command\BackfillShipping', 'addons\shopro_wechat_trade\command\RetryFailed', + 'addons\shopro_wechat_trade\command\ConfirmOrders', 'addons\shopro_wechat_trade\command\VerifyInstall', 'addons\shopro_wechat_trade\command\InstallFull', 'addons\shopro_aliyun_express\command\InstallFull',