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.
175 lines
8.0 KiB
175 lines
8.0 KiB
<?php
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use App\Models\ActivityDay;
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Support\Facades\DB;
|
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
|
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
|
|
|
|
class ExportVenueActivityStatsCommand extends Command
|
|
{
|
|
protected $signature = 'activities:export-venue-stats
|
|
{--from=2026-07-01 : 场次开始日期 Y-m-d}
|
|
{--to=2026-07-31 : 场次结束日期 Y-m-d}
|
|
{--output= : 导出 xlsx 路径}';
|
|
|
|
protected $description = '导出指定日期范围内各场馆活动汇总(按场馆一行)及场次明细 Excel';
|
|
|
|
public function handle(): int
|
|
{
|
|
$start = (string) $this->option('from');
|
|
$end = (string) $this->option('to');
|
|
$out = (string) ($this->option('output') ?: storage_path(
|
|
'app/export_venues_activities_'.str_replace('-', '', $start).'_'.str_replace('-', '', $end).'.xlsx'
|
|
));
|
|
|
|
$regPeople = "SUM(CASE WHEN reservations.status <> 'cancelled' THEN COALESCE(reservations.ticket_count, 1) ELSE 0 END)";
|
|
$regOrders = "SUM(CASE WHEN reservations.status <> 'cancelled' THEN 1 ELSE 0 END)";
|
|
$cancelOrders = "SUM(CASE WHEN reservations.status = 'cancelled' THEN 1 ELSE 0 END)";
|
|
$cancelPeople = "SUM(CASE WHEN reservations.status = 'cancelled' THEN COALESCE(reservations.ticket_count, 1) ELSE 0 END)";
|
|
$verifiedPeople = "SUM(CASE WHEN reservations.status = 'verified' THEN COALESCE(reservations.ticket_count, 1) ELSE 0 END)";
|
|
$pendingPeople = "SUM(CASE WHEN reservations.status = 'pending' THEN COALESCE(reservations.ticket_count, 1) ELSE 0 END)";
|
|
$expiredPeople = "SUM(CASE WHEN reservations.status = 'expired' THEN COALESCE(reservations.ticket_count, 1) ELSE 0 END)";
|
|
|
|
$baseJoin = function ($q) {
|
|
$q->join('activities', function ($j) {
|
|
$j->on('activities.id', '=', 'activity_days.activity_id')->whereNull('activities.deleted_at');
|
|
})
|
|
->join('venues', 'venues.id', '=', 'activities.venue_id')
|
|
->join('reservations', function ($j) {
|
|
$j->on('reservations.activity_day_id', '=', 'activity_days.id')
|
|
->whereNull('reservations.deleted_at')
|
|
->where(function ($qq) {
|
|
$qq->whereNull('reservations.reservation_kind')
|
|
->orWhere('reservations.reservation_kind', 'activity');
|
|
});
|
|
});
|
|
};
|
|
|
|
$venueRows = ActivityDay::query()
|
|
->select([
|
|
'venues.id as venue_id',
|
|
'venues.name as venue_name',
|
|
DB::raw("GROUP_CONCAT(DISTINCT DATE_FORMAT(activity_days.activity_date, '%Y-%m-%d') ORDER BY activity_days.activity_date SEPARATOR ', ') as activity_dates"),
|
|
DB::raw('COUNT(DISTINCT activities.id) as activity_count'),
|
|
DB::raw('COUNT(DISTINCT activity_days.id) as session_count'),
|
|
DB::raw("{$regOrders} as registration_orders"),
|
|
DB::raw("{$regPeople} as registration_people"),
|
|
DB::raw("{$cancelOrders} as cancelled_orders"),
|
|
DB::raw("{$cancelPeople} as cancelled_people"),
|
|
DB::raw("{$verifiedPeople} as verified_people"),
|
|
])
|
|
->tap($baseJoin)
|
|
->whereBetween('activity_days.activity_date', [$start, $end])
|
|
->groupBy('venues.id', 'venues.name')
|
|
->havingRaw("{$regOrders} > 0 OR {$cancelOrders} > 0")
|
|
->orderBy('venues.name')
|
|
->get();
|
|
|
|
$sessionRows = ActivityDay::query()
|
|
->select([
|
|
'venues.id as venue_id',
|
|
'venues.name as venue_name',
|
|
'activities.id as activity_id',
|
|
'activities.title as activity_title',
|
|
'activity_days.id as activity_day_id',
|
|
'activity_days.session_name',
|
|
'activity_days.activity_date',
|
|
'activity_days.session_start_at',
|
|
'activity_days.session_end_at',
|
|
'activity_days.day_quota',
|
|
'activity_days.booked_count',
|
|
DB::raw("{$regOrders} as registration_orders"),
|
|
DB::raw("{$regPeople} as registration_people"),
|
|
DB::raw("{$cancelOrders} as cancelled_orders"),
|
|
DB::raw("{$cancelPeople} as cancelled_people"),
|
|
DB::raw("{$verifiedPeople} as verified_people"),
|
|
DB::raw("{$pendingPeople} as pending_people"),
|
|
DB::raw("{$expiredPeople} as expired_people"),
|
|
])
|
|
->tap($baseJoin)
|
|
->whereBetween('activity_days.activity_date', [$start, $end])
|
|
->groupBy(
|
|
'venues.id',
|
|
'venues.name',
|
|
'activities.id',
|
|
'activities.title',
|
|
'activity_days.id',
|
|
'activity_days.session_name',
|
|
'activity_days.activity_date',
|
|
'activity_days.session_start_at',
|
|
'activity_days.session_end_at',
|
|
'activity_days.day_quota',
|
|
'activity_days.booked_count'
|
|
)
|
|
->havingRaw("{$regOrders} > 0 OR {$cancelOrders} > 0")
|
|
->orderBy('venues.name')
|
|
->orderBy('activity_days.activity_date')
|
|
->orderBy('activity_days.session_start_at')
|
|
->orderBy('activities.id')
|
|
->get();
|
|
|
|
$spreadsheet = new Spreadsheet;
|
|
$sheet1 = $spreadsheet->getActiveSheet();
|
|
$sheet1->setTitle('场馆汇总');
|
|
$sheet1Data = [['场馆ID', '场馆名称', '活动日期', '活动数量', '场次数量', '报名人数', '报名总人数', '已取消数', '已取消人数', '到场人数(已核销)']];
|
|
foreach ($venueRows as $r) {
|
|
$sheet1Data[] = [
|
|
$r->venue_id,
|
|
$r->venue_name,
|
|
$r->activity_dates,
|
|
(int) $r->activity_count,
|
|
(int) $r->session_count,
|
|
(int) $r->registration_orders,
|
|
(int) $r->registration_people,
|
|
(int) $r->cancelled_orders,
|
|
(int) $r->cancelled_people,
|
|
(int) $r->verified_people,
|
|
];
|
|
}
|
|
$sheet1->fromArray($sheet1Data, null, 'A1');
|
|
|
|
$sheet2 = $spreadsheet->createSheet();
|
|
$sheet2->setTitle('场次信息');
|
|
$sheet2Data = [['场馆ID', '场馆名称', '活动ID', '活动名称', '场次ID', '场次名称', '活动日期', '场次开始时间', '场次结束时间', '场次名额', '已占用名额', '报名人数', '报名总人数', '已取消数', '已取消人数', '已核销人数', '待核销人数', '已过期人数']];
|
|
foreach ($sessionRows as $r) {
|
|
$sheet2Data[] = [
|
|
$r->venue_id,
|
|
$r->venue_name,
|
|
$r->activity_id,
|
|
$r->activity_title,
|
|
$r->activity_day_id,
|
|
$r->session_name,
|
|
$r->activity_date ? (string) $r->activity_date : '',
|
|
$r->session_start_at ? (string) $r->session_start_at : '',
|
|
$r->session_end_at ? (string) $r->session_end_at : '',
|
|
$r->day_quota,
|
|
$r->booked_count,
|
|
(int) $r->registration_orders,
|
|
(int) $r->registration_people,
|
|
(int) $r->cancelled_orders,
|
|
(int) $r->cancelled_people,
|
|
(int) $r->verified_people,
|
|
(int) $r->pending_people,
|
|
(int) $r->expired_people,
|
|
];
|
|
}
|
|
$sheet2->fromArray($sheet2Data, null, 'A1');
|
|
|
|
$dir = dirname($out);
|
|
if (! is_dir($dir)) {
|
|
mkdir($dir, 0755, true);
|
|
}
|
|
|
|
(new Xlsx($spreadsheet))->save($out);
|
|
|
|
$this->info("场馆汇总:{$venueRows->count()} 行");
|
|
$this->info("场次信息:{$sessionRows->count()} 行");
|
|
$this->info("Excel 已生成:{$out}");
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
}
|