user(); abort_unless($user && ($user->isSuperAdmin() || $user->role === 'venue_admin'), 403, '无权限'); $data = $request->validate([ 'name' => ['required', 'string', 'max:120'], 'venue_type' => ['nullable', 'string', 'max:80'], 'venue_types' => ['nullable', 'array'], 'venue_types.*' => ['string', 'max:80'], 'unit_name' => ['nullable', 'string', 'max:120'], 'district' => ['nullable', 'string', 'max:80'], 'ticket_type' => ['nullable', 'string', 'max:80'], 'appointment_type' => ['nullable', 'string', 'max:40'], 'open_mode' => ['nullable', 'string', 'max:40', Rule::in(['fulltime', 'scheduled', 'appointment'])], 'open_time' => ['nullable', 'string', 'max:120'], 'reservation_notice' => ['nullable', 'string'], 'ticket_content' => ['nullable', 'string'], 'booking_method' => ['nullable', 'string'], 'visit_form' => ['nullable', 'string'], 'consultation_hours' => ['nullable', 'string'], 'address' => ['nullable', 'string', 'max:255'], 'contact_phone' => ['nullable', 'string', 'max:20'], 'lat' => ['nullable', 'numeric'], 'lng' => ['nullable', 'numeric'], 'cover_image' => ['nullable', 'string', 'max:255'], 'gallery_media' => ['nullable', 'array'], 'gallery_media.*.type' => ['required_with:gallery_media', 'in:image,video'], 'gallery_media.*.url' => ['required_with:gallery_media', 'string', 'max:255'], 'detail_html' => ['nullable', 'string'], 'live_people_count' => ['nullable', 'integer', 'min:0'], 'sort' => ['nullable', 'integer', 'min:0'], 'is_active' => ['boolean'], ]); $auditStatus = $user->isSuperAdmin() ? Venue::AUDIT_APPROVED : Venue::AUDIT_PENDING; $venue = Venue::create($data + [ 'is_active' => $data['is_active'] ?? true, 'audit_status' => $auditStatus, 'audit_remark' => null, 'last_approved_snapshot' => null, ]); if (! $user->isSuperAdmin()) { $user->venues()->syncWithoutDetaching([$venue->id]); } return response()->json($venue->fresh(), 201); } public function index(Request $request): JsonResponse { $user = $request->user(); $keyword = trim((string) $request->string('keyword')); $district = trim((string) $request->string('district')); $venueType = trim((string) $request->string('venue_type')); $ticketType = trim((string) $request->string('ticket_type')); $openMode = trim((string) $request->string('open_mode')); $isActive = $request->input('is_active'); $auditStatus = trim((string) $request->string('audit_status')); if ($user->isSuperAdmin()) { $query = Venue::query(); } else { $query = $user->venues(); } if ($keyword !== '') { $query->where(function ($q) use ($keyword) { $q->where('name', 'like', '%' . $keyword . '%') ->orWhere('address', 'like', '%' . $keyword . '%') ->orWhere('unit_name', 'like', '%' . $keyword . '%') ->orWhere('open_time', 'like', '%' . $keyword . '%') ->orWhere('reservation_notice', 'like', '%' . $keyword . '%') ->orWhere('ticket_content', 'like', '%' . $keyword . '%') ->orWhere('booking_method', 'like', '%' . $keyword . '%') ->orWhere('visit_form', 'like', '%' . $keyword . '%') ->orWhere('consultation_hours', 'like', '%' . $keyword . '%'); }); } if ($district !== '') { $query->where('district', $district); } if ($venueType !== '') { $query->where(function ($q) use ($venueType) { $q->where('venue_type', $venueType) ->orWhereJsonContains('venue_types', $venueType); }); } if ($ticketType !== '') { $query->where('ticket_type', $ticketType); } if ($openMode !== '') { $query->where('open_mode', $openMode); } if ($isActive !== null && $isActive !== '') { $query->where('is_active', (int) $isActive === 1); } if ($auditStatus !== '') { $query->where('audit_status', $auditStatus); } $venues = $query->orderBy('venues.sort')->orderByDesc('venues.id')->get(); return response()->json($venues); } public function update(Request $request, int $id): JsonResponse { $venue = Venue::query()->findOrFail($id); $this->ensureVenuePermission($request, $venue->id); $user = $request->user(); $data = $request->validate([ 'name' => ['sometimes', 'string', 'max:120'], 'venue_type' => ['nullable', 'string', 'max:80'], 'venue_types' => ['nullable', 'array'], 'venue_types.*' => ['string', 'max:80'], 'unit_name' => ['nullable', 'string', 'max:120'], 'district' => ['nullable', 'string', 'max:80'], 'ticket_type' => ['nullable', 'string', 'max:80'], 'appointment_type' => ['nullable', 'string', 'max:40'], 'open_mode' => ['nullable', 'string', 'max:40', Rule::in(['fulltime', 'scheduled', 'appointment'])], 'open_time' => ['nullable', 'string', 'max:120'], 'reservation_notice' => ['nullable', 'string'], 'ticket_content' => ['nullable', 'string'], 'booking_method' => ['nullable', 'string'], 'visit_form' => ['nullable', 'string'], 'consultation_hours' => ['nullable', 'string'], 'address' => ['nullable', 'string', 'max:255'], 'contact_phone' => ['nullable', 'string', 'max:20'], 'lat' => ['nullable', 'numeric'], 'lng' => ['nullable', 'numeric'], 'cover_image' => ['nullable', 'string', 'max:255'], 'gallery_media' => ['nullable', 'array'], 'gallery_media.*.type' => ['required_with:gallery_media', 'in:image,video'], 'gallery_media.*.url' => ['required_with:gallery_media', 'string', 'max:255'], 'detail_html' => ['nullable', 'string'], 'live_people_count' => ['nullable', 'integer', 'min:0'], 'sort' => ['nullable', 'integer', 'min:0'], 'is_active' => ['boolean'], ]); if (! $user->isSuperAdmin()) { unset($data['sort']); if ($venue->audit_status === Venue::AUDIT_APPROVED) { $venue->last_approved_snapshot = $venue->buildAuditSnapshot(); } $data['audit_status'] = Venue::AUDIT_PENDING; $data['audit_remark'] = null; } else { $data['audit_status'] = Venue::AUDIT_APPROVED; $data['audit_remark'] = null; $data['last_approved_snapshot'] = null; } $venue->fill($data)->save(); return response()->json($venue->fresh()); } public function approve(Request $request, int $id): JsonResponse { abort_unless($request->user()?->isSuperAdmin(), 403, '仅超级管理员可审核'); $venue = Venue::query()->findOrFail($id); $venue->audit_status = Venue::AUDIT_APPROVED; $venue->audit_remark = null; $venue->last_approved_snapshot = null; $venue->save(); return response()->json($venue->fresh()); } public function reject(Request $request, int $id): JsonResponse { abort_unless($request->user()?->isSuperAdmin(), 403, '仅超级管理员可审核'); $data = $request->validate([ 'remark' => ['nullable', 'string', 'max:2000'], ]); $venue = Venue::query()->findOrFail($id); $venue->audit_status = Venue::AUDIT_REJECTED; $venue->audit_remark = $data['remark'] ?? null; $venue->save(); return response()->json($venue->fresh()); } private function ensureVenuePermission(Request $request, int $venueId): void { $user = $request->user(); if ($user->isSuperAdmin()) { return; } $allowed = $user->venues()->where('venues.id', $venueId)->exists(); abort_unless($allowed, 403, '仅可操作已绑定场馆'); } }