ensurePlatformAdmin($request); $filters = [ 'status' => trim((string) $request->query('status', '')), 'keyword' => trim((string) $request->query('keyword', '')), 'merchant_id' => trim((string) $request->query('merchant_id', '')), 'plan_id' => trim((string) $request->query('plan_id', '')), 'expiry' => trim((string) $request->query('expiry', '')), ]; $query = $this->applyFilters( SiteSubscription::query()->with(['merchant', 'plan'])->withCount('platformOrders'), $filters )->orderBy('id'); $filename = 'site_subscriptions_' . now()->format('Ymd_His') . '.csv'; return response()->streamDownload(function () use ($query) { $out = fopen('php://output', 'w'); // UTF-8 BOM,避免 Excel 打开中文乱码 fwrite($out, "\xEF\xBB\xBF"); fputcsv($out, [ 'ID', '订阅号', '站点', '套餐', '关联订单数', '状态', '计费周期', '周期(月)', '金额', '开始时间', '到期时间', '到期状态', '试用到期', '生效时间', '取消时间', ]); $statusLabels = $this->statusLabels(); $query->chunkById(500, function ($subs) use ($out, $statusLabels) { foreach ($subs as $sub) { $endsAt = $sub->ends_at; $expiryLabel = '无到期'; if ($endsAt) { if ($endsAt->lt(now())) { $expiryLabel = '已过期'; } elseif ($endsAt->lt(now()->addDays(7))) { $expiryLabel = '7天内到期'; } else { $expiryLabel = '未到期'; } } $status = (string) ($sub->status ?? ''); $statusText = ($statusLabels[$status] ?? $status); $statusText = $statusText . ' (' . $status . ')'; fputcsv($out, [ $sub->id, $sub->subscription_no, $sub->merchant?->name ?? '', $sub->plan_name ?: ($sub->plan?->name ?? ''), (int) ($sub->platform_orders_count ?? 0), $statusText, $sub->billing_cycle ?: '', (int) $sub->period_months, (float) $sub->amount, optional($sub->starts_at)->format('Y-m-d H:i:s') ?: '', optional($sub->ends_at)->format('Y-m-d H:i:s') ?: '', $expiryLabel, optional($sub->trial_ends_at)->format('Y-m-d H:i:s') ?: '', optional($sub->activated_at)->format('Y-m-d H:i:s') ?: '', optional($sub->cancelled_at)->format('Y-m-d H:i:s') ?: '', ]); } }); fclose($out); }, $filename, [ 'Content-Type' => 'text/csv; charset=UTF-8', ]); } public function index(Request $request): View { $this->ensurePlatformAdmin($request); $filters = [ 'status' => trim((string) $request->query('status', '')), 'keyword' => trim((string) $request->query('keyword', '')), 'merchant_id' => trim((string) $request->query('merchant_id', '')), 'plan_id' => trim((string) $request->query('plan_id', '')), // 到期辅助筛选(不改变 status 字段,仅按 ends_at 计算) // - expired:已过期(ends_at < now) // - expiring_7d:7 天内到期(now <= ends_at < now+7d) 'expiry' => trim((string) $request->query('expiry', '')), ]; $query = $this->applyFilters( SiteSubscription::query()->with(['merchant', 'plan'])->withCount('platformOrders'), $filters ); $subscriptions = (clone $query) ->latest('id') ->paginate(10) ->withQueryString(); $baseQuery = $this->applyFilters(SiteSubscription::query(), $filters); return view('admin.site_subscriptions.index', [ 'subscriptions' => $subscriptions, 'filters' => $filters, 'statusLabels' => $this->statusLabels(), 'filterOptions' => [ 'statuses' => $this->statusLabels(), ], 'merchants' => SiteSubscription::query()->with('merchant')->select('merchant_id')->distinct()->get()->pluck('merchant')->filter()->unique('id')->values(), 'plans' => SiteSubscription::query()->with('plan')->select('plan_id')->whereNotNull('plan_id')->distinct()->get()->pluck('plan')->filter()->unique('id')->values(), 'summaryStats' => [ 'total_subscriptions' => (clone $baseQuery)->count(), 'activated_subscriptions' => (clone $baseQuery)->where('status', 'activated')->count(), 'pending_subscriptions' => (clone $baseQuery)->where('status', 'pending')->count(), 'cancelled_subscriptions' => (clone $baseQuery)->where('status', 'cancelled')->count(), // 可治理辅助指标:按 ends_at 计算 'expired_subscriptions' => (clone $baseQuery) ->whereNotNull('ends_at') ->where('ends_at', '<', now()) ->count(), 'expiring_7d_subscriptions' => (clone $baseQuery) ->whereNotNull('ends_at') ->where('ends_at', '>=', now()) ->where('ends_at', '<', now()->addDays(7)) ->count(), ], ]); } protected function statusLabels(): array { return [ 'pending' => '待生效', 'activated' => '已生效', 'cancelled' => '已取消', 'expired' => '已过期', ]; } protected function applyFilters(Builder $query, array $filters): Builder { return $query ->when($filters['status'] !== '', fn (Builder $builder) => $builder->where('status', $filters['status'])) ->when(($filters['merchant_id'] ?? '') !== '', fn (Builder $builder) => $builder->where('merchant_id', (int) $filters['merchant_id'])) ->when(($filters['plan_id'] ?? '') !== '', fn (Builder $builder) => $builder->where('plan_id', (int) $filters['plan_id'])) ->when(($filters['expiry'] ?? '') !== '', function (Builder $builder) use ($filters) { $expiry = (string) ($filters['expiry'] ?? ''); if ($expiry === 'expired') { $builder->whereNotNull('ends_at')->where('ends_at', '<', now()); } elseif ($expiry === 'expiring_7d') { $builder->whereNotNull('ends_at') ->where('ends_at', '>=', now()) ->where('ends_at', '<', now()->addDays(7)); } }) ->when($filters['keyword'] !== '', function (Builder $builder) use ($filters) { // 关键词搜索:订阅号 / 站点 / 套餐 / 计费周期 $keyword = trim((string) ($filters['keyword'] ?? '')); if ($keyword === '') { return; } $builder->where(function (Builder $q) use ($keyword) { $q->where('subscription_no', 'like', '%' . $keyword . '%') ->orWhere('plan_name', 'like', '%' . $keyword . '%') ->orWhere('billing_cycle', 'like', '%' . $keyword . '%') ->orWhereHas('merchant', function (Builder $mq) use ($keyword) { $mq->where('name', 'like', '%' . $keyword . '%') ->orWhere('slug', 'like', '%' . $keyword . '%'); }) ->orWhereHas('plan', function (Builder $pq) use ($keyword) { $pq->where('name', 'like', '%' . $keyword . '%') ->orWhere('code', 'like', '%' . $keyword . '%'); }); if (ctype_digit($keyword)) { $q->orWhere('id', (int) $keyword); } }); }); } }