feat(shopro_wechat_trade): 添加自提订单准备功能 - 在ConfirmOrders命令中添加prepareSelfetchOrder方法,用于处理自提订单的发货逻辑 - 当订单项为自提类型且未发货时,调用checkDispatchAndSend进行发货操作 - 添加异常处理机制,确保自提订单处理失败时不会中断整体流程 - 增加dry-run模式支持,便于测试验证 - 添加日志输出,提供详细的执行状态反馈 ```
232 行
8.8 KiB
PHP
232 行
8.8 KiB
PHP
<?php
|
||
|
||
namespace addons\shopro_wechat_trade\command;
|
||
|
||
use addons\shopro\model\Order;
|
||
use addons\shopro\model\OrderItem;
|
||
use addons\shopro_wechat_trade\library\Bridge;
|
||
use addons\shopro_wechat_trade\library\SyncLogger;
|
||
use addons\shopro_wechat_trade\library\WxShippingService;
|
||
use think\console\Command;
|
||
use think\console\Input;
|
||
use think\console\input\Option;
|
||
use think\console\Output;
|
||
use think\Db;
|
||
|
||
/**
|
||
* 商城确认收货 + 微信 notify_confirm_receive(按 order_sn / order_id)
|
||
*/
|
||
class ConfirmOrders extends Command
|
||
{
|
||
protected function configure()
|
||
{
|
||
$this->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;
|
||
}
|
||
|
||
try {
|
||
$this->prepareSelfetchOrder($orderModel, $order, $dryRun, $output);
|
||
} catch (\Exception $e) {
|
||
$output->writeln(' [FAIL] 自提准备: ' . $e->getMessage());
|
||
$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));
|
||
}
|
||
|
||
/**
|
||
* 自提/到店:未发货时先走 selfetchSendItem,便于 upload_shipping_info(logistics_type=4)
|
||
*/
|
||
protected function prepareSelfetchOrder(Order $orderModel, &$order, $dryRun, Output $output)
|
||
{
|
||
$needsShip = false;
|
||
foreach ($order->item ?? [] as $item) {
|
||
$itemArr = is_object($item) && method_exists($item, 'toArray') ? $item->toArray() : (array) $item;
|
||
if (($itemArr['dispatch_type'] ?? '') !== 'selfetch') {
|
||
continue;
|
||
}
|
||
if (intval($itemArr['dispatch_status'] ?? 0) < OrderItem::DISPATCH_STATUS_SENDED) {
|
||
$needsShip = true;
|
||
break;
|
||
}
|
||
}
|
||
if (!$needsShip) {
|
||
return;
|
||
}
|
||
if ($dryRun) {
|
||
$output->writeln(' [dry-run] 自提发货 checkDispatchAndSend');
|
||
return;
|
||
}
|
||
$orderModel->checkDispatchAndSend($order, []);
|
||
$order = Order::with('item')->where('id', $order['id'])->find();
|
||
if (!$order) {
|
||
throw new \RuntimeException('自提发货后订单不存在');
|
||
}
|
||
$output->writeln(' [OK] 自提已发货(待同步微信 upload_shipping_info)');
|
||
sleep(2);
|
||
}
|
||
|
||
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'] ?? ''));
|
||
}
|
||
}
|
||
}
|