argument('date'); // 验证日期格式 try { $serviceDate = Carbon::createFromFormat('Y-m-d', $date); } catch (\Exception $e) { $this->error("日期格式错误,请使用 Y-m-d 格式,例如:2025-12-01"); return 1; } $serviceDateStr = $serviceDate->format('Y-m-d'); $serviceDateEnd = $serviceDateStr . ' 23:59:59'; $isDryRun = $this->option('dry-run'); if ($isDryRun) { $this->warn("=== 试运行模式:将只显示预览信息,不会执行实际回滚 ==="); $this->line(''); } $this->info("开始" . ($isDryRun ? "预览" : "回滚") . " {$serviceDateStr} 的订单项修复..."); // 查询需要回滚的记录 // 条件:有 paid_at_original(说明被修复过),且 paid_at 等于修复后的值 $items = OrderItems::where('service_date', $serviceDateStr) ->whereNotNull('paid_at_original') ->where('paid_at', $serviceDateEnd) ->with(['order' => function ($query) { $query->select('id', 'customer_id'); }]) ->get(); $totalCount = $items->count(); $this->info("找到 {$totalCount} 条需要回滚的记录"); if ($totalCount == 0) { $this->info("没有需要回滚的记录"); return 0; } $rolledBackCount = 0; $errorCount = 0; $previewData = []; if (!$isDryRun) { $bar = $this->output->createProgressBar($totalCount); $bar->start(); } else { $this->line(''); $this->info("预览将要回滚的记录:"); $this->line(''); } foreach ($items as $item) { try { // 获取客户ID $customerId = $item->order ? $item->order->customer_id : null; if (!$customerId) { if (!$isDryRun) { $this->line(''); $this->warn("订单项 {$item->id} 没有关联的客户,跳过"); $bar->advance(); } $errorCount++; continue; } $originalPaidAt = $item->paid_at_original; $currentPaidAt = $item->paid_at; // 检查是否有关联的 balance 记录 $balanceRecord = Balance::where('belongs_type', OrderItems::class) ->where('belongs_id', $item->id) ->where('customer_id', $customerId) ->first(); if ($isDryRun) { // 试运行模式:只收集预览信息 $previewData[] = [ 'order_item_id' => $item->id, 'order_id' => $item->order_id, 'customer_id' => $customerId, 'service_date' => $item->service_date, 'current_paid_at' => $currentPaidAt, 'original_paid_at' => $originalPaidAt, 'has_balance_record' => $balanceRecord ? '是' : '否', 'balance_record_id' => $balanceRecord ? $balanceRecord->id : null, 'balance_current_created_at' => $balanceRecord ? $balanceRecord->created_at : null, ]; $rolledBackCount++; } else { // 实际执行回滚 DB::beginTransaction(); try { // 恢复 paid_at $item->update([ 'paid_at' => $originalPaidAt, 'paid_at_original' => null // 清空,表示已回滚 ]); // 恢复关联的 balance 记录的 created_at if ($balanceRecord) { $balanceRecord->update([ 'created_at' => $originalPaidAt ]); } else { $this->line(''); $this->warn("订单项 {$item->id} 没有找到关联的 balance 记录"); } DB::commit(); $rolledBackCount++; Log::info("回滚订单项 {$item->id}:paid_at {$currentPaidAt} -> {$originalPaidAt}"); } catch (\Exception $e) { DB::rollBack(); $errorCount++; $this->line(''); $this->error("回滚订单项 {$item->id} 失败:" . $e->getMessage()); Log::error("回滚订单项 {$item->id} 失败:" . $e->getMessage()); } } } catch (\Exception $e) { $errorCount++; if (!$isDryRun) { $this->line(''); $this->error("处理订单项 {$item->id} 时出错:" . $e->getMessage()); } Log::error("处理订单项 {$item->id} 时出错:" . $e->getMessage()); } if (!$isDryRun) { $bar->advance(); } } if (!$isDryRun) { $bar->finish(); } $this->line(''); $this->line(''); // 试运行模式:显示详细预览信息 if ($isDryRun && !empty($previewData)) { $this->info("=== 预览详情 ==="); $this->line(''); $this->info("将要回滚的记录(" . count($previewData) . " 条):"); $this->line(''); $this->table( ['订单项ID', '订单ID', '客户ID', '服务日期', '当前paid_at', '回滚后paid_at', '有balance记录', 'balance当前created_at'], array_map(function($item) { return [ $item['order_item_id'], $item['order_id'], $item['customer_id'], $item['service_date'], $item['current_paid_at'], $item['original_paid_at'], $item['has_balance_record'], $item['balance_current_created_at'] ?: '-', ]; }, $previewData) ); $this->line(''); } // 输出统计信息 $this->info(($isDryRun ? "预览" : "回滚") . "完成!"); $this->table( ['统计项', '数量'], [ ['总记录数', $totalCount], ['成功' . ($isDryRun ? '(将回滚)' : '回滚'), $rolledBackCount], ['处理失败', $errorCount], ] ); if ($isDryRun) { $this->line(''); $this->comment("提示:这是试运行模式,没有执行实际回滚。"); $this->comment("要执行实际回滚,请运行命令时不加 --dry-run 参数。"); } return 0; } }