|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace App\Http\Controllers\Api;
|
|
|
|
|
|
|
|
|
|
use App\Http\Controllers\Controller;
|
|
|
|
|
use App\Models\Venue;
|
|
|
|
|
use Illuminate\Http\JsonResponse;
|
|
|
|
|
use Illuminate\Http\Request;
|
|
|
|
|
use Illuminate\Validation\Rule;
|
|
|
|
|
|
|
|
|
|
class VenueController extends Controller
|
|
|
|
|
{
|
|
|
|
|
public function store(Request $request): JsonResponse
|
|
|
|
|
{
|
|
|
|
|
$user = $request->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, '仅可操作已绑定场馆');
|
|
|
|
|
}
|
|
|
|
|
}
|