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.

87 lines
2.8 KiB

<?php
namespace App\Support;
use App\Models\Activity;
use App\Models\VerifyPortalCredential;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
final class ActivityVerifyPortalPin
{
/** verify_portal_credentials 中占位用户名,核销登录仅校验 6 位数字密码并按活动发卡 */
public const CRED_USERNAME = '__pin__';
/** 随机 6 位数字口令,与场馆抢票口令一起全库唯一(含软删活动占用) */
public static function generateUnique(): string
{
return PortalSixDigitPin::generateUnique();
}
public static function syncPinCredential(Activity $activity): VerifyPortalCredential
{
$pin = trim((string) ($activity->verify_portal_pin ?? ''));
if ($pin === '' || ! preg_match('/^\d{6}$/', $pin)) {
throw new \InvalidArgumentException('活动核销口令无效');
}
/** @var VerifyPortalCredential|null $cred */
$cred = VerifyPortalCredential::query()
->where('portal_kind', VerifyPortalCredential::KIND_ACTIVITY)
->where('portal_id', $activity->id)
->where('username', self::CRED_USERNAME)
->first();
$attributes = [
'venue_id' => (int) $activity->venue_id,
'password' => Hash::make($pin),
'password_plain_enc' => Crypt::encryptString($pin),
'note' => null,
];
if ($cred !== null) {
$cred->forceFill($attributes)->save();
return $cred;
}
return VerifyPortalCredential::query()->create(array_merge([
'portal_kind' => VerifyPortalCredential::KIND_ACTIVITY,
'portal_id' => (int) $activity->id,
'username' => self::CRED_USERNAME,
], $attributes));
}
/** 补齐 activities.verify_portal_pin 占位凭证行 */
public static function ensure(Activity $activity): string
{
$activity->refresh();
$pinOut = trim((string) ($activity->verify_portal_pin ?? ''));
if ($pinOut !== '' && preg_match('/^\d{6}$/', $pinOut)) {
self::syncPinCredential($activity);
return $pinOut;
}
DB::transaction(function () use ($activity): void {
$fresh = Activity::query()->whereKey($activity->id)->lockForUpdate()->first();
if ($fresh === null) {
return;
}
$p = trim((string) ($fresh->verify_portal_pin ?? ''));
if ($p !== '' && preg_match('/^\d{6}$/', $p)) {
return;
}
$fresh->forceFill(['verify_portal_pin' => self::generateUnique()])->saveQuietly();
});
$activity->refresh();
$pinOut = trim((string) ($activity->verify_portal_pin ?? ''));
self::syncPinCredential($activity);
return $pinOut;
}
}