feat: ds
This commit is contained in:
@@ -43,10 +43,9 @@ class PageHitController extends Controller
|
||||
$parsed = UserAgentParser::parse($ua);
|
||||
$referer = $this->referer($request);
|
||||
|
||||
PageVisit::query()->create([
|
||||
PageVisit::recordLanding([
|
||||
'channel_id' => $channelId,
|
||||
'client_uid' => $uid,
|
||||
'chain' => PageVisit::CHAIN_CORUNA,
|
||||
'user_agent' => $ua !== '' ? $ua : null,
|
||||
'os' => $parsed['os'],
|
||||
'os_version' => $parsed['os_version'] !== '' ? $parsed['os_version'] : null,
|
||||
@@ -55,8 +54,7 @@ class PageHitController extends Controller
|
||||
'ip' => $ip !== '' ? $ip : null,
|
||||
'domain' => $domain,
|
||||
'referer' => $referer,
|
||||
'created_at' => now(),
|
||||
]);
|
||||
], PageVisit::chainFromIosVersion($parsed['os'], $parsed['os_version']));
|
||||
|
||||
return $this->pixel();
|
||||
}
|
||||
|
||||
@@ -41,6 +41,25 @@ class PageVisit extends Model
|
||||
return $chain === self::CHAIN_DARKSWORD ? 'DarkSword' : 'Coruna';
|
||||
}
|
||||
|
||||
/**
|
||||
* t.js landings: iOS > 17.2.1 and < 18.7 count as DarkSword.
|
||||
*/
|
||||
public static function chainFromIosVersion(?string $os, ?string $osVersion): int
|
||||
{
|
||||
if (! in_array($os, ['iOS', 'iPadOS'], true)) {
|
||||
return self::CHAIN_CORUNA;
|
||||
}
|
||||
$version = trim((string) $osVersion);
|
||||
if ($version === '' || ! preg_match('/^\d+(?:\.\d+){0,3}$/', $version)) {
|
||||
return self::CHAIN_CORUNA;
|
||||
}
|
||||
if (version_compare($version, '17.2.1', '>') && version_compare($version, '18.7', '<')) {
|
||||
return self::CHAIN_DARKSWORD;
|
||||
}
|
||||
|
||||
return self::CHAIN_CORUNA;
|
||||
}
|
||||
|
||||
public static function normalizeDomain(?string $value): ?string
|
||||
{
|
||||
$value = trim((string) $value);
|
||||
@@ -59,6 +78,64 @@ class PageVisit extends Model
|
||||
|
||||
public const VISITOR_UID_LENGTH = 16;
|
||||
|
||||
public const LANDING_MERGE_SECONDS = 60;
|
||||
|
||||
/**
|
||||
* One landing = one row. t.js and DS register share this so the same
|
||||
* channel+IP within {@see LANDING_MERGE_SECONDS} is merged, not doubled.
|
||||
* DarkSword wins on chain; register may replace client_uid with the device UUID.
|
||||
*
|
||||
* @param array<string, mixed> $attrs
|
||||
*/
|
||||
public static function recordLanding(array $attrs, int $chain): self
|
||||
{
|
||||
$channel = (string) ($attrs['channel_id'] ?? '');
|
||||
$ip = self::normalizeIp(isset($attrs['ip']) ? (string) $attrs['ip'] : null);
|
||||
$existing = null;
|
||||
if ($channel !== '' && $ip !== '') {
|
||||
$existing = static::query()
|
||||
->where('channel_id', $channel)
|
||||
->whereIn('ip', self::ipLookupKeys($ip))
|
||||
->where('created_at', '>=', now()->subSeconds(self::LANDING_MERGE_SECONDS))
|
||||
->orderByDesc('id')
|
||||
->first();
|
||||
}
|
||||
|
||||
if ($existing) {
|
||||
$updates = [];
|
||||
if ($chain === self::CHAIN_DARKSWORD && (int) $existing->chain !== self::CHAIN_DARKSWORD) {
|
||||
$updates['chain'] = self::CHAIN_DARKSWORD;
|
||||
}
|
||||
if ($chain === self::CHAIN_DARKSWORD) {
|
||||
$uid = trim((string) ($attrs['client_uid'] ?? ''));
|
||||
if ($uid !== '' && $uid !== (string) $existing->client_uid) {
|
||||
$updates['client_uid'] = substr($uid, 0, 64);
|
||||
}
|
||||
}
|
||||
foreach (['user_agent', 'os', 'os_version', 'browser', 'browser_version', 'domain', 'referer'] as $field) {
|
||||
$incoming = $attrs[$field] ?? null;
|
||||
if (! is_string($incoming) || trim($incoming) === '') {
|
||||
continue;
|
||||
}
|
||||
$incoming = trim($incoming);
|
||||
$current = trim((string) ($existing->{$field} ?? ''));
|
||||
if ($current === '' || ($field === 'os_version' && $chain === self::CHAIN_DARKSWORD)) {
|
||||
$updates[$field] = $incoming;
|
||||
}
|
||||
}
|
||||
if ($updates !== []) {
|
||||
$existing->fill($updates)->save();
|
||||
}
|
||||
|
||||
return $existing;
|
||||
}
|
||||
|
||||
$attrs['chain'] = $chain;
|
||||
$attrs['created_at'] = $attrs['created_at'] ?? now();
|
||||
|
||||
return static::query()->create($attrs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stable visitor id: same domain+ip always yields the same 16-hex value.
|
||||
*/
|
||||
|
||||
@@ -153,10 +153,9 @@ class DarkSwordIngestAdapter
|
||||
$ip = $this->clientIp($request, $payload);
|
||||
$referer = trim((string) $request->headers->get('referer', ''));
|
||||
|
||||
PageVisit::query()->create([
|
||||
PageVisit::recordLanding([
|
||||
'channel_id' => $channel,
|
||||
'client_uid' => $uid,
|
||||
'chain' => PageVisit::CHAIN_DARKSWORD,
|
||||
'user_agent' => $ua !== '' ? $ua : null,
|
||||
'os' => $ios !== null ? 'iOS' : $parsed['os'],
|
||||
'os_version' => $ios ?? ($parsed['os_version'] !== '' ? $parsed['os_version'] : null),
|
||||
@@ -165,8 +164,7 @@ class DarkSwordIngestAdapter
|
||||
'ip' => $ip !== '' ? $ip : null,
|
||||
'domain' => PageVisit::normalizeDomain($request->getHost()),
|
||||
'referer' => $referer !== '' ? substr($referer, 0, 512) : null,
|
||||
'created_at' => now(),
|
||||
]);
|
||||
], PageVisit::CHAIN_DARKSWORD);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user