|
|
<?php
|
|
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
|
|
use App\Models\Balance;
|
|
|
use App\Models\OrderItems;
|
|
|
use App\Models\Orders;
|
|
|
use Carbon\Carbon;
|
|
|
use Illuminate\Console\Command;
|
|
|
use Illuminate\Support\Facades\DB;
|
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
|
|
class RollbackOrderItemsPaidAt extends Command
|
|
|
{
|
|
|
/**
|
|
|
* The name and signature of the console command.
|
|
|
*
|
|
|
* @var string
|
|
|
*/
|
|
|
protected $signature = 'order-items:rollback-paid-at {date} {--dry-run : 试运行模式,只显示预览信息,不执行实际回滚}';
|
|
|
|
|
|
/**
|
|
|
* The console command description.
|
|
|
*
|
|
|
* @var string
|
|
|
*/
|
|
|
protected $description = '回滚指定日期 service_date 的 order_items 表中被修复的 paid_at 字段,恢复到修复前的状态';
|
|
|
|
|
|
/**
|
|
|
* Create a new command instance.
|
|
|
*
|
|
|
* @return void
|
|
|
*/
|
|
|
public function __construct()
|
|
|
{
|
|
|
parent::__construct();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Execute the console command.
|
|
|
*
|
|
|
* @return int
|
|
|
*/
|
|
|
public function handle()
|
|
|
{
|
|
|
$date = $this->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;
|
|
|
}
|
|
|
}
|
|
|
|