You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

103 lines
4.1 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
namespace App\Console\Commands;
use App\Jobs\SendAppointCar;
use App\Models\Appointment;
use App\Models\ThirdAppointmentLog;
use Illuminate\Console\Command;
class SyncAppointmentCarAppointments extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'sync_appointment_car_appointments';
/**
* The console command description.
*
* @var string
*/
protected $description = '同步预约未提交的新增车牌号';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$now = date('Y-m-d H:i:s');
$total = 0;
// 只处理还没开始的、已审核通过的预约。
// 车牌预约是发给第三方停车系统的,过期预约不需要补发;未审核通过的预约也不能提前发起车牌预约。
$appointments = Appointment::with('user')
->where('status', 1)
->where('start_time', '>', $now)
// 用户后续在个人资料里绑定了车牌,才有可能需要补发。
->whereHas('user', function ($query) {
$query->whereNotNull('plate')
->where('plate', '!=', '');
})->get();
foreach ($appointments as $appointment) {
// 用户当前车牌:以 users.plate 为准,表示用户后来补充/更新后的完整车牌列表。
$userPlates = $this->parsePlates($appointment->user->plate);
// 预约记录车牌:表示当时提交预约时已经记录过的车牌列表。
$appointmentPlates = $this->parsePlates($appointment->plate);
// 只找用户后来新增、但预约记录里没有的车牌,避免重复提交老车牌。
$newPlates = array_values(array_diff($userPlates, $appointmentPlates));
// 需要确认是否预约成功的车牌:以用户当前车牌为准。
// 即使车牌已经回写到 appointments.plate只要第三方没有成功记录后续任务仍然应该继续补发。
$pendingPlates = $userPlates;
if (empty($newPlates) && empty($pendingPlates)) {
continue;
}
if (!empty($newPlates)) {
// 把新增车牌回写到 appointments.plate保持预约记录和用户当前车牌一致。
// 注意:补发判断不能只依赖 appointments.plate否则队列失败后会因为已回写而不再补发。
$appointment->plate = implode(',', array_values(array_unique(array_merge($appointmentPlates, $newPlates))));
$appointment->save();
}
foreach ($pendingPlates as $plate) {
// 第三方日志里如果已经有这个预约 + 车牌的成功记录,说明之前已经预约成功。
// 这里再做一次幂等校验,防止手动补数据、任务重复执行或队列重试导致重复预约。
$hasAppointment = ThirdAppointmentLog::where('appointment_id', $appointment->id)
->where('plate', $plate)
->where('plate_status', 1)
->exists();
if ($hasAppointment) {
continue;
}
// 没有成功记录才投递队列任务,由 SendAppointCar 调用第三方停车系统发起预约。
dispatch(new SendAppointCar($appointment, $plate));
$total++;
}
}
$this->info("预约车牌同步完成,共提交 {$total} 条车牌预约");
return self::SUCCESS;
}
/**
* 解析车牌字符串。
*
* 数据库存储格式是多个车牌用英文逗号分隔例如苏E12345,苏E67890。
* 这里统一做 trim、去空值、去重兼容空字符串、null、以及用户输入时带空格的情况。
*/
protected function parsePlates($plate)
{
return array_values(array_unique(array_filter(array_map('trim', explode(',', (string) $plate)))));
}
}