argument('activity_id'); $perDay = max(1, (int) $this->option('per-day')); $dryRun = (bool) $this->option('dry-run'); $activity = Activity::query()->find($activityId); if (!$activity) { $this->error("未找到活动 id={$activityId}"); return self::FAILURE; } if (!$activity->start_at || !$activity->end_at) { $this->error('该活动缺少 start_at / end_at,无法按日期遍历。请先在后台为活动设置开始、结束时间。'); return self::FAILURE; } $start = $activity->start_at->copy()->startOfDay(); $end = $activity->end_at->copy()->startOfDay(); $this->info("活动 #{$activity->id} {$activity->title}"); $this->line("场馆 venue_id={$activity->venue_id}"); $this->line("日期范围:{$start->toDateString()} ~ {$end->toDateString()}(含首尾)"); $this->line("每日生成预约:{$perDay} 条".($dryRun ? '(dry-run)' : '')); $days = []; for ($d = $start->copy(); $d->lte($end); $d->addDay()) { $days[] = $d->copy(); } $this->table( ['序号', '活动日', '将生成条数'], collect($days)->map(fn (Carbon $c, $i) => [$i + 1, $c->toDateString(), $perDay])->all() ); if ($dryRun) { $this->warn('已跳过写入(--dry-run)。去掉该参数后执行将写入数据库。'); return self::SUCCESS; } $created = 0; DB::transaction(function () use ($activity, $days, $perDay, &$created) { $dayIndex = 0; foreach ($days as $day) { $dateStr = $day->toDateString(); $dayIndex++; $activityDay = ActivityDay::query() ->where('activity_id', $activity->id) ->whereDate('activity_date', $dateStr) ->first(); if (!$activityDay) { $opensAt = $day->copy()->subDays(7)->setTime(9, 0, 0); $activityDay = ActivityDay::create([ 'activity_id' => $activity->id, 'activity_date' => $dateStr, 'day_quota' => max($perDay * 2, 30), 'booked_count' => 0, 'opens_at' => $opensAt, ]); $this->line("已创建活动日场次:{$dateStr},activity_day_id={$activityDay->id}"); } $ticketEach = 1; $needBooked = $activityDay->booked_count + ($perDay * $ticketEach); if ($activityDay->day_quota < $needBooked) { $activityDay->day_quota = $needBooked + 10; $activityDay->save(); $this->line("已调高 {$dateStr} 放票数至 {$activityDay->day_quota}"); } for ($i = 1; $i <= $perDay; $i++) { $phone = $this->makeUniquePhone($activity->id, $dayIndex, $i); $res = Reservation::create([ 'venue_id' => $activity->venue_id, 'activity_id' => $activity->id, 'activity_day_id' => $activityDay->id, 'visitor_name' => "测试用户{$dateStr}-{$i}", 'visitor_phone' => $phone, 'qr_token' => (string) Str::uuid(), 'status' => 'pending', 'ticket_count' => $ticketEach, 'reservation_source' => 'legacy', ]); $created++; $activityDay->increment('booked_count', $ticketEach); $this->line(" + 预约 #{$res->id} {$phone} → activity_day {$activityDay->id}"); } } Activity::refreshRegisteredCountFromReservations($activity->id); }); $this->info("完成:新建 {$created} 条预约;活动 registered_count 已按已预约人数合计更新。"); return self::SUCCESS; } private function makeUniquePhone(int $activityId, int $dayIndex, int $i): string { // 11 位手机号:138 + 8 位数字,保证脚本内不易重复 $n = ($activityId * 10000 + $dayIndex * 100 + $i) % 100000000; return '138'.str_pad((string) $n, 8, '0', STR_PAD_LEFT); } }