ensureActivityVenueAdmin($request, $activity); if ($activity->verify_portal_token === null || $activity->verify_portal_token === '') { $activity->forceFill(['verify_portal_token' => (string) \Illuminate\Support\Str::uuid()])->save(); } VerifyPortalCode::ensureForActivity($activity); $activity->refresh(); $rows = VerifyPortalCredential::query() ->where('portal_kind', VerifyPortalCredential::KIND_ACTIVITY) ->where('portal_id', $activity->id) ->with('venue:id,name') ->orderBy('id') ->get() ->map(fn (VerifyPortalCredential $c) => $this->credentialToRow($c)); return response()->json([ 'verify_portal_code' => $activity->verify_portal_code, 'credentials' => $rows, ]); } public function activityStore(Request $request, Activity $activity): JsonResponse { $this->ensureActivityVenueAdmin($request, $activity); $data = $request->validate([ 'username' => ['required', 'string', 'max:64'], 'password' => ['required', 'string', 'min:6', 'max:72'], 'note' => ['nullable', 'string', 'max:255'], ]); $vid = (int) $activity->venue_id; $exists = VerifyPortalCredential::query() ->where('portal_kind', VerifyPortalCredential::KIND_ACTIVITY) ->where('portal_id', $activity->id) ->where('venue_id', $vid) ->where('username', $data['username']) ->exists(); if ($exists) { return response()->json(['message' => '该场馆下此用户名已存在'], 422); } $plain = (string) $data['password']; $c = VerifyPortalCredential::query()->create([ 'portal_kind' => VerifyPortalCredential::KIND_ACTIVITY, 'portal_id' => $activity->id, 'venue_id' => $vid, 'username' => $data['username'], 'password' => Hash::make($plain), 'password_plain_enc' => Crypt::encryptString($plain), 'note' => $data['note'] ?? null, ]); return response()->json(['id' => $c->id], 201); } public function activityUpdate(Request $request, Activity $activity, VerifyPortalCredential $verifyPortalCredential): JsonResponse { $this->ensureActivityVenueAdmin($request, $activity); $this->assertCredentialBelongsActivity($verifyPortalCredential, $activity); $data = $request->validate([ 'password' => ['sometimes', 'string', 'min:6', 'max:72'], 'note' => ['sometimes', 'nullable', 'string', 'max:255'], ]); if (array_key_exists('password', $data)) { $plain = (string) $data['password']; $verifyPortalCredential->password = Hash::make($plain); $verifyPortalCredential->password_plain_enc = Crypt::encryptString($plain); } if (array_key_exists('note', $data)) { $verifyPortalCredential->note = $data['note']; } $verifyPortalCredential->save(); return response()->json(['message' => '已更新']); } public function activityDestroy(Request $request, Activity $activity, VerifyPortalCredential $verifyPortalCredential): JsonResponse { $this->ensureActivityVenueAdmin($request, $activity); $this->assertCredentialBelongsActivity($verifyPortalCredential, $activity); $verifyPortalCredential->tokens()->delete(); $verifyPortalCredential->delete(); return response()->json(['message' => '已删除']); } public function ticketGrabShow(Request $request, TicketGrabEvent $ticketGrabEvent): JsonResponse { $this->ensureTicketGrabAdmin($request, $ticketGrabEvent); if ($ticketGrabEvent->verify_portal_token === null || $ticketGrabEvent->verify_portal_token === '') { $ticketGrabEvent->forceFill(['verify_portal_token' => (string) \Illuminate\Support\Str::uuid()])->save(); } VerifyPortalCode::ensureForTicketGrabEvent($ticketGrabEvent); $ticketGrabEvent->refresh(); $rows = VerifyPortalCredential::query() ->where('portal_kind', VerifyPortalCredential::KIND_TICKET_GRAB) ->where('portal_id', $ticketGrabEvent->id) ->with('venue:id,name') ->orderBy('venue_id') ->orderBy('id') ->get() ->map(fn (VerifyPortalCredential $c) => $this->credentialToRow($c)); return response()->json([ 'verify_portal_code' => $ticketGrabEvent->verify_portal_code, 'credentials' => $rows, ]); } public function ticketGrabStore(Request $request, TicketGrabEvent $ticketGrabEvent): JsonResponse { $this->ensureTicketGrabAdmin($request, $ticketGrabEvent); $data = $request->validate([ 'venue_id' => ['required', 'integer'], 'username' => ['required', 'string', 'max:64'], 'password' => ['required', 'string', 'min:6', 'max:72'], 'note' => ['nullable', 'string', 'max:255'], ]); $vid = (int) $data['venue_id']; $pivotOk = TicketGrabEventVenue::query() ->where('ticket_grab_event_id', $ticketGrabEvent->id) ->where('venue_id', $vid) ->exists(); abort_unless($pivotOk, 422, '场馆未参与本抢票活动'); $exists = VerifyPortalCredential::query() ->where('portal_kind', VerifyPortalCredential::KIND_TICKET_GRAB) ->where('portal_id', $ticketGrabEvent->id) ->where('venue_id', $vid) ->where('username', $data['username']) ->exists(); if ($exists) { return response()->json(['message' => '该场馆下此用户名已存在'], 422); } $plain = (string) $data['password']; $c = VerifyPortalCredential::query()->create([ 'portal_kind' => VerifyPortalCredential::KIND_TICKET_GRAB, 'portal_id' => $ticketGrabEvent->id, 'venue_id' => $vid, 'username' => $data['username'], 'password' => Hash::make($plain), 'password_plain_enc' => Crypt::encryptString($plain), 'note' => $data['note'] ?? null, ]); return response()->json(['id' => $c->id], 201); } public function ticketGrabUpdate( Request $request, TicketGrabEvent $ticketGrabEvent, VerifyPortalCredential $verifyPortalCredential, ): JsonResponse { $this->ensureTicketGrabAdmin($request, $ticketGrabEvent); $this->assertCredentialBelongsTicketGrab($verifyPortalCredential, $ticketGrabEvent); $data = $request->validate([ 'password' => ['sometimes', 'string', 'min:6', 'max:72'], 'note' => ['sometimes', 'nullable', 'string', 'max:255'], ]); if (array_key_exists('password', $data)) { $plain = (string) $data['password']; $verifyPortalCredential->password = Hash::make($plain); $verifyPortalCredential->password_plain_enc = Crypt::encryptString($plain); } if (array_key_exists('note', $data)) { $verifyPortalCredential->note = $data['note']; } $verifyPortalCredential->save(); return response()->json(['message' => '已更新']); } public function ticketGrabDestroy( Request $request, TicketGrabEvent $ticketGrabEvent, VerifyPortalCredential $verifyPortalCredential, ): JsonResponse { $this->ensureTicketGrabAdmin($request, $ticketGrabEvent); $this->assertCredentialBelongsTicketGrab($verifyPortalCredential, $ticketGrabEvent); $verifyPortalCredential->tokens()->delete(); $verifyPortalCredential->delete(); return response()->json(['message' => '已删除']); } /** * @return array */ private function credentialToRow(VerifyPortalCredential $c): array { $plain = null; if ($c->password_plain_enc !== null && $c->password_plain_enc !== '') { try { $plain = Crypt::decryptString($c->password_plain_enc); } catch (\Throwable) { $plain = null; } } return [ 'id' => $c->id, 'venue_id' => $c->venue_id, 'venue_name' => $c->venue?->name, 'username' => $c->username, 'password_plain' => $plain, 'note' => $c->note, 'created_at' => $c->created_at?->toDateTimeString(), ]; } private function ensureActivityVenueAdmin(Request $request, Activity $activity): void { $user = $request->user(); if ($user->isSuperAdmin()) { return; } $allowed = $user->venues()->where('venues.id', $activity->venue_id)->exists(); abort_unless($allowed, 403, '仅可操作已绑定场馆'); } private function ensureTicketGrabAdmin(Request $request, TicketGrabEvent $e): void { $pivots = TicketGrabEventVenue::query() ->where('ticket_grab_event_id', $e->id) ->pluck('venue_id') ->all(); if ($pivots === []) { return; } $user = $request->user(); if ($user->isSuperAdmin()) { return; } $allow = $user->venues()->pluck('venues.id'); foreach (array_unique(array_map('intval', $pivots)) as $id) { if ($id > 0 && ! $allow->contains($id)) { abort(403, '仅可操作已绑定场馆'); } } } private function assertCredentialBelongsActivity(VerifyPortalCredential $c, Activity $activity): void { abort_unless( $c->portal_kind === VerifyPortalCredential::KIND_ACTIVITY && (int) $c->portal_id === (int) $activity->id, 404 ); } private function assertCredentialBelongsTicketGrab(VerifyPortalCredential $c, TicketGrabEvent $e): void { abort_unless( $c->portal_kind === VerifyPortalCredential::KIND_TICKET_GRAB && (int) $c->portal_id === (int) $e->id, 404 ); } }