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.
145 lines
5.9 KiB
145 lines
5.9 KiB
<?php
|
|
|
|
namespace App\Http\Controllers\Api;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\Activity;
|
|
use App\Models\TicketGrabEvent;
|
|
use App\Models\Venue;
|
|
use App\Models\VerifyPortalCredential;
|
|
use App\Support\ActivityVerifyPortalPin;
|
|
use App\Support\VenueVerifyPortalPin;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Hash;
|
|
|
|
class VerifyPortalAuthController extends Controller
|
|
{
|
|
/**
|
|
* 历史抢票短链 ?v= 入口已废弃;统一使用与活动相同的核销页 + 6 位口令。
|
|
*/
|
|
public function portalPreview(Request $request): JsonResponse
|
|
{
|
|
return response()->json(['message' => '请使用平台统一核销入口'], 404);
|
|
}
|
|
|
|
public function login(Request $request): JsonResponse
|
|
{
|
|
$data = $request->validate([
|
|
'portal_code' => ['nullable', 'string', 'max:16'],
|
|
'portal_token' => ['nullable', 'string', 'max:64'],
|
|
'username' => ['nullable', 'string', 'max:64'],
|
|
'password' => ['required', 'string', 'max:72'],
|
|
]);
|
|
|
|
$password = (string) ($data['password'] ?? '');
|
|
$passwordTrim = trim($password);
|
|
$usernameInput = trim((string) ($data['username'] ?? ''));
|
|
|
|
if (preg_match('/^\d{6}$/', $passwordTrim)) {
|
|
if ($usernameInput !== '') {
|
|
return response()->json(['message' => '核销只需输入 6 位数字口令,无需用户名'], 422);
|
|
}
|
|
|
|
/** @var Activity|null $activity */
|
|
$activity = Activity::query()->where('verify_portal_pin', $passwordTrim)->first();
|
|
if ($activity !== null) {
|
|
$cred = ActivityVerifyPortalPin::syncPinCredential($activity);
|
|
if (! Hash::check($passwordTrim, $cred->password)) {
|
|
$cred = ActivityVerifyPortalPin::syncPinCredential($activity->fresh());
|
|
if (! Hash::check($passwordTrim, $cred->password)) {
|
|
return response()->json(['message' => '系统繁忙,请稍后再试'], 500);
|
|
}
|
|
}
|
|
|
|
if (! $cred->portalAcceptsVerification()) {
|
|
return response()->json(['message' => '活动已结束,无法登录核销'], 422);
|
|
}
|
|
|
|
$expiresAt = now()->addMonths(6);
|
|
/** 活动核销允许多人并行在线 */
|
|
$plain = $cred->createToken('verify-portal', ['verify-portal'], $expiresAt)->plainTextToken;
|
|
|
|
return response()->json([
|
|
'token' => $plain,
|
|
'auth_mode' => 'verify_portal',
|
|
'portal_kind' => VerifyPortalCredential::KIND_ACTIVITY,
|
|
'portal_id' => (int) $activity->id,
|
|
]);
|
|
}
|
|
|
|
/** @var Venue|null $venue */
|
|
$venue = Venue::query()->where('verify_portal_pin', $passwordTrim)->first();
|
|
if ($venue !== null) {
|
|
$cred = VenueVerifyPortalPin::syncPinCredential($venue);
|
|
if (! Hash::check($passwordTrim, $cred->password)) {
|
|
$cred = VenueVerifyPortalPin::syncPinCredential($venue->fresh());
|
|
if (! Hash::check($passwordTrim, $cred->password)) {
|
|
return response()->json(['message' => '系统繁忙,请稍后再试'], 500);
|
|
}
|
|
}
|
|
|
|
if (! $cred->portalAcceptsVerification()) {
|
|
return response()->json(['message' => '该场馆核销入口不可用(未启用或未通过审核),无法登录核销'], 422);
|
|
}
|
|
|
|
$expiresAt = now()->addMonths(6);
|
|
/** 与活动一致:不因新登录吊销旧 token */
|
|
$plain = $cred->createToken('verify-portal', ['verify-portal'], $expiresAt)->plainTextToken;
|
|
|
|
return response()->json([
|
|
'token' => $plain,
|
|
'auth_mode' => 'verify_portal',
|
|
'portal_kind' => VerifyPortalCredential::KIND_TICKET_GRAB_VENUE,
|
|
'portal_id' => (int) $venue->id,
|
|
]);
|
|
}
|
|
|
|
return response()->json(['message' => '密码错误'], 422);
|
|
}
|
|
|
|
return response()->json([
|
|
'message' => '请使用后台账号用户名与密码登录,或直接输入 6 位数字核销口令(活动或抢票场馆口令)。',
|
|
], 422);
|
|
}
|
|
|
|
public function me(Request $request): JsonResponse
|
|
{
|
|
$cred = $request->user();
|
|
if (! $cred instanceof VerifyPortalCredential) {
|
|
return response()->json(['message' => '非核销门户登录态'], 403);
|
|
}
|
|
|
|
if (! $cred->portalAcceptsVerification()) {
|
|
$cred->tokens()->delete();
|
|
|
|
return response()->json(['message' => '核销已失效'], 403);
|
|
}
|
|
|
|
$cred->load('venue:id,name');
|
|
$title = '';
|
|
if ($cred->portal_kind === VerifyPortalCredential::KIND_ACTIVITY) {
|
|
$title = (string) Activity::query()->where('id', $cred->portal_id)->value('title');
|
|
} elseif ($cred->portal_kind === VerifyPortalCredential::KIND_TICKET_GRAB_VENUE) {
|
|
$title = '抢票 · '.(string) ($cred->venue?->name ?? '');
|
|
} else {
|
|
$title = (string) TicketGrabEvent::query()->where('id', $cred->portal_id)->value('title');
|
|
}
|
|
|
|
$hidePinUser = \in_array(
|
|
(string) $cred->username,
|
|
[ActivityVerifyPortalPin::CRED_USERNAME, VenueVerifyPortalPin::CRED_USERNAME],
|
|
true,
|
|
);
|
|
|
|
return response()->json([
|
|
'auth_mode' => 'verify_portal',
|
|
'portal_kind' => $cred->portal_kind,
|
|
'portal_id' => $cred->portal_id,
|
|
'username' => $hidePinUser ? null : $cred->username,
|
|
'venue' => $cred->venue ? ['id' => $cred->venue->id, 'name' => $cred->venue->name] : null,
|
|
'event_title' => $title,
|
|
]);
|
|
}
|
|
}
|