```
feat(shopro_wechat_trade): 添加订单确认收货命令 新增 confirm-orders 命令用于商城确认收货并同步微信 notify_confirm_receive,支持批量处理指定订单。 添加了以下功能选项: - 支持通过 --sn 参数指定订单号进行处理 - 支持通过 --skip-mall 参数跳过商城确认,仅处理微信同步 - 支持 dry-run 模式预览操作 - 在 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
|
||||
```
|
||||
|
||||
## 触发点
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
<?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;
|
||||
}
|
||||
|
||||
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'] ?? ''));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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',
|
||||
|
||||
在新工单中引用
屏蔽一个用户