refactor: 移除 qr_issued_at 字段

QR payload 改为实时生成,不存储发放时间。
前端 localStorage 自行管理缓存。
feat/phase4-ticket-wallet
Council 2026-04-23 14:37:10 +08:00
parent 6903522b5a
commit ac676d00be
3 changed files with 12 additions and 98 deletions

View File

@ -13,34 +13,6 @@
return array ( return array (
'listen' => 'listen' =>
array ( array (
'plugins_service_admin_menu_data' =>
array (
0 => 'app\\plugins\\vr_ticket\\Hook',
),
'plugins_service_order_pay_success_handle_end' =>
array (
0 => 'app\\plugins\\vr_ticket\\Hook',
),
'plugins_service_order_delete_success' =>
array (
0 => 'app\\plugins\\vr_ticket\\Hook',
),
'plugins_view_admin_goods_save' =>
array (
0 => 'app\\plugins\\vr_ticket\\hook\\AdminGoodsSave',
),
'plugins_service_goods_save_handle' =>
array (
0 => 'app\\plugins\\vr_ticket\\hook\\AdminGoodsSaveHandle',
),
'plugins_service_goods_save_thing_end' =>
array (
0 => 'app\\plugins\\vr_ticket\\hook\\AdminGoodsSaveHandle',
),
'plugins_css_data' =>
array (
0 => 'app\\plugins\\vr_ticket\\hook\\ViewGoodsCss',
),
), ),
); );
?> ?>

View File

@ -30,7 +30,6 @@ CREATE TABLE IF NOT EXISTS `{{prefix}}vr_tickets` (
`verify_time` INT UNSIGNED DEFAULT 0 COMMENT '核销时间', `verify_time` INT UNSIGNED DEFAULT 0 COMMENT '核销时间',
`verifier_id` BIGINT UNSIGNED DEFAULT 0 COMMENT '核销员ID', `verifier_id` BIGINT UNSIGNED DEFAULT 0 COMMENT '核销员ID',
`issued_at` INT UNSIGNED DEFAULT 0 COMMENT '票发放时间', `issued_at` INT UNSIGNED DEFAULT 0 COMMENT '票发放时间',
`qr_issued_at` INT UNSIGNED DEFAULT 0 COMMENT 'QR发放时间戳',
`created_at` INT UNSIGNED DEFAULT 0 COMMENT '创建时间', `created_at` INT UNSIGNED DEFAULT 0 COMMENT '创建时间',
`updated_at` INT UNSIGNED DEFAULT 0 COMMENT '更新时间', `updated_at` INT UNSIGNED DEFAULT 0 COMMENT '更新时间',
PRIMARY KEY (`id`), PRIMARY KEY (`id`),

View File

@ -19,12 +19,6 @@ class WalletService extends BaseService
*/ */
const QR_TTL = 1800; // 30分钟 const QR_TTL = 1800; // 30分钟
/**
* QR 刷新阈值(秒)
* 剩余有效期 > 此值时返回缓存
*/
const QR_REFRESH_THRESHOLD = 900; // 15分钟
/** /**
* 获取用户所有票 * 获取用户所有票
* *
@ -79,8 +73,6 @@ class WalletService extends BaseService
'verify_status' => $ticket['verify_status'], 'verify_status' => $ticket['verify_status'],
'issued_at' => $ticket['issued_at'], 'issued_at' => $ticket['issued_at'],
'short_code' => $shortCode, 'short_code' => $shortCode,
// 是否需要刷新 QR
'qr_need_refresh' => self::qrNeedsRefresh($ticket['qr_issued_at'] ?? 0),
]; ];
} }
@ -141,10 +133,7 @@ class WalletService extends BaseService
/** /**
* 生成 QR payload * 生成 QR payload
* *
* 缓存策略: * QR 有效期 30 分钟,动态生成,不存储
* - QR 有效期 30 分钟
* - 剩余有效期 > 15 分钟:返回缓存
* - 剩余有效期 15 分钟:刷新
* *
* @param array $ticket 票数据 * @param array $ticket 票数据
* @return array ['payload' => string, 'expires_at' => int, 'expires_in' => int] * @return array ['payload' => string, 'expires_at' => int, 'expires_in' => int]
@ -152,51 +141,27 @@ class WalletService extends BaseService
public static function getQrPayload(array $ticket): array public static function getQrPayload(array $ticket): array
{ {
$now = time(); $now = time();
$issuedAt = $ticket['qr_issued_at'] ?? 0; $expiresAt = $now + self::QR_TTL;
$expiresAt = $issuedAt + self::QR_TTL;
// 检查是否需要刷新 $payload = [
$needsRefresh = ($issuedAt == 0) || (($expiresAt - $now) <= self::QR_REFRESH_THRESHOLD); 'id' => $ticket['id'],
'g' => $ticket['goods_id'],
'iat' => $now,
'exp' => $expiresAt,
];
if ($needsRefresh) { $encoded = self::signQrPayload($payload);
// 生成新 QR
$issuedAt = $now;
$expiresAt = $now + self::QR_TTL;
$payload = [
'id' => $ticket['id'],
'g' => $ticket['goods_id'],
'iat' => $issuedAt,
'exp' => $expiresAt,
];
$encoded = self::signQrPayload($payload);
// 回写数据库(更新 qr_issued_at
\think\facade\Db::name('vr_tickets')
->where('id', $ticket['id'])
->update(['qr_issued_at' => $issuedAt]);
} else {
// 返回缓存的 payload
// 重新构建 payload从数据库读取 iat
$payload = [
'id' => $ticket['id'],
'g' => $ticket['goods_id'],
'iat' => $issuedAt,
'exp' => $expiresAt,
];
$encoded = self::signQrPayload($payload);
}
return [ return [
'payload' => $encoded, 'payload' => $encoded,
'expires_at' => $expiresAt, 'expires_at' => $expiresAt,
'expires_in' => max(0, $expiresAt - $now), 'expires_in' => self::QR_TTL,
]; ];
} }
/** /**
* 强制刷新 QR payload * 强制刷新 QR payload
* 重新生成一个新的 QR payload有效期重新计算
* *
* @param int $ticketId 票ID * @param int $ticketId 票ID
* @param int $userId 用户ID * @param int $userId 用户ID
@ -204,32 +169,10 @@ class WalletService extends BaseService
*/ */
public static function refreshQrPayload(int $ticketId, int $userId): ?array public static function refreshQrPayload(int $ticketId, int $userId): ?array
{ {
// 先清零 qr_issued_at强制刷新 // 直接调用 getTicketDetail它会重新生成 QR
\think\facade\Db::name('vr_tickets')
->where('id', $ticketId)
->update(['qr_issued_at' => 0]);
return self::getTicketDetail($ticketId, $userId); return self::getTicketDetail($ticketId, $userId);
} }
/**
* 检查 QR 是否需要刷新
*
* @param int $qrIssuedAt QR 发放时间戳
* @return bool
*/
public static function qrNeedsRefresh(int $qrIssuedAt): bool
{
if ($qrIssuedAt == 0) {
return true;
}
$now = time();
$expiresAt = $qrIssuedAt + self::QR_TTL;
return (($expiresAt - $now) <= self::QR_REFRESH_THRESHOLD);
}
/** /**
* 解析座位信息 * 解析座位信息
* *