109 lines
3.4 KiB
PHP
109 lines
3.4 KiB
PHP
<?php
|
||
require_once __DIR__ . '/ClientIpResolver.php';
|
||
require_once __DIR__ . '/FpUrlHelper.php';
|
||
|
||
/**
|
||
* 二次风控(byApiRisk)Session 缓存:同 Session 内复用判定结果,IP 变化时失效。
|
||
*/
|
||
class RiskSecondaryCache
|
||
{
|
||
const SESSION_KEY = 'cloak_risk_cache';
|
||
|
||
/**
|
||
* @param array{result:string,reason?:string,fp_url?:string,ip?:string} $payload
|
||
*/
|
||
public static function store(array $payload)
|
||
{
|
||
$ip = ClientIpResolver::normalizeIp($payload['ip'] ?? ClientIpResolver::resolve());
|
||
if ($ip === null) {
|
||
return;
|
||
}
|
||
|
||
$_SESSION[self::SESSION_KEY] = [
|
||
'ip' => $ip,
|
||
'result' => ($payload['result'] ?? '') === 'true' ? 'true' : 'false',
|
||
'reason' => (string) ($payload['reason'] ?? ''),
|
||
'fp_url' => (string) ($payload['fp_url'] ?? ''),
|
||
'risk_number' => defined('CLOAK_RISK_NUMBER') ? (int) CLOAK_RISK_NUMBER : 0,
|
||
'campaign_id' => defined('COSTM_IP_SCORE') ? (string) COSTM_IP_SCORE : '',
|
||
'cached_at' => time(),
|
||
];
|
||
}
|
||
|
||
/**
|
||
* @param string|null $currentIp 为空时使用 ClientIpResolver::resolve()
|
||
* @return array|null
|
||
*/
|
||
public static function get($currentIp = null)
|
||
{
|
||
if (empty($_SESSION[self::SESSION_KEY]) || !is_array($_SESSION[self::SESSION_KEY])) {
|
||
return null;
|
||
}
|
||
|
||
$cached = $_SESSION[self::SESSION_KEY];
|
||
$ip = ClientIpResolver::normalizeIp($currentIp !== null && $currentIp !== '' ? $currentIp : ClientIpResolver::resolve());
|
||
$cachedIp = ClientIpResolver::normalizeIp($cached['ip'] ?? '');
|
||
|
||
$invalidate = false;
|
||
if ($ip === null || $cachedIp === null || $ip !== $cachedIp) {
|
||
$invalidate = true;
|
||
} elseif (defined('CLOAK_RISK_NUMBER') && (int) ($cached['risk_number'] ?? -1) !== (int) CLOAK_RISK_NUMBER) {
|
||
$invalidate = true;
|
||
} elseif (defined('COSTM_IP_SCORE') && (string) ($cached['campaign_id'] ?? '') !== (string) COSTM_IP_SCORE) {
|
||
$invalidate = true;
|
||
}
|
||
|
||
if ($invalidate) {
|
||
self::clear();
|
||
$_SESSION['visit_to_3'] = '1';
|
||
return null;
|
||
}
|
||
|
||
return $cached;
|
||
}
|
||
|
||
public static function clear()
|
||
{
|
||
unset($_SESSION[self::SESSION_KEY]);
|
||
}
|
||
|
||
/**
|
||
* 将缓存同步到 visit_to_3 / check_result,供 RouteResolver 等复用。
|
||
*
|
||
* @param array $cached
|
||
*/
|
||
public static function applyToSession(array $cached)
|
||
{
|
||
$_SESSION['visit_to_3'] = 3;
|
||
$_SESSION['check_result'] = ($cached['result'] ?? '') === 'true' ? 'true' : 'false';
|
||
}
|
||
|
||
public static function hitReason()
|
||
{
|
||
return '二次风控缓存命中';
|
||
}
|
||
|
||
/**
|
||
* @param array $cached
|
||
* @return array{result:bool,reason:string,link:string}
|
||
*/
|
||
public static function toApiResponse(array $cached)
|
||
{
|
||
$passed = ($cached['result'] ?? '') === 'true';
|
||
$link = (string) ($cached['fp_url'] ?? '');
|
||
|
||
if ($passed && $link === '') {
|
||
$link = FpUrlHelper::fallbackUrl();
|
||
}
|
||
if (!$passed && $link === '' && defined('DB_ZP')) {
|
||
$link = (string) DB_ZP;
|
||
}
|
||
|
||
return [
|
||
'result' => $passed,
|
||
'reason' => self::hitReason(),
|
||
'link' => $link,
|
||
];
|
||
}
|
||
}
|