validate([ 'mobile' => ['required', 'regex:/^1[3-9]\d{9}$/'], 'code' => ['required', 'string', 'max:64'], 'competition_slug' => ['required', 'string', 'max:64'], ]); $user = DB::transaction(function () use ($data) { $verification = SmsVerification::query() ->where('scene', SmsVerification::SCENE_PARTICIPANT_LOGIN) ->where('mobile', $data['mobile']) ->where('status', SmsVerification::STATUS_SENT) ->where('expires_at', '>', now()) ->latest('id') ->lockForUpdate() ->first(); if ($verification === null || $verification->code !== $data['code']) { throw ValidationException::withMessages([ 'code' => ['验证码无效或已过期'], ]); } $competition = Competition::query() ->where('slug', $data['competition_slug']) ->where('published', true) ->first(); if ($competition === null) { throw ValidationException::withMessages([ 'competition_slug' => ['赛事不存在或未发布'], ]); } $user = User::query()->firstOrCreate( ['mobile' => $data['mobile']], ['name' => null, 'email' => null, 'password' => null] ); $verification->forceFill([ 'status' => SmsVerification::STATUS_USED, 'used_at' => now(), ])->save(); SmsVerification::query() ->where('scene', SmsVerification::SCENE_PARTICIPANT_LOGIN) ->where('mobile', $data['mobile']) ->where('id', '<>', $verification->id) ->whereIn('status', [ SmsVerification::STATUS_PENDING, SmsVerification::STATUS_SENT, ]) ->update(['status' => SmsVerification::STATUS_EXPIRED]); return $user; }); $user->tokens()->where('name', 'audience')->delete(); $token = $user->createToken('audience')->plainTextToken; return response()->json([ 'token' => $token, 'token_type' => 'Bearer', 'user' => [ 'id' => $user->id, 'mobile' => $user->mobile, 'name' => $user->name, ], ]); } }