Files

128 lines
4.8 KiB
PHP

<?php
namespace App\Integrations\Whmcs\Support;
use Illuminate\Support\Facades\DB;
/**
* Resolves a WHMCS `tblclients.id` to the matching local `customers.id`: by the uuid both
* sides share (see ClientWriter), falling back to email for customers that already existed
* on the portal with a different uuid.
*/
class CustomerIdResolver
{
/** @var array<int, int>|null whmcs client id => local customer id */
private static ?array $map = null;
/** @var array<int, true>|null whmcs client ids that exist in tblclients */
private static ?array $known = null;
public function __construct(private SyncReport $report)
{
}
public function resolve(int $whmcsClientId): ?int
{
self::$map ??= $this->build();
return self::$map[$whmcsClientId] ?? null;
}
public function resolveFor(string $entity, string|int $ref, int $whmcsClientId): ?int
{
$customerId = $this->resolve($whmcsClientId);
if ($customerId === null) {
$exists = isset(self::$known[$whmcsClientId]);
$this->report->note(
$entity,
(string) $ref,
$whmcsClientId,
$exists ? 'khach_chua_co_tren_portal' : 'khach_khong_ton_tai_trong_whmcs',
$exists
? "Khách WHMCS #{$whmcsClientId} chưa có tài khoản portal khớp uuid/email — bỏ qua {$entity}."
: "Khách WHMCS #{$whmcsClientId} không còn trong tblclients (đã xoá) — bỏ qua {$entity}.",
);
}
return $customerId;
}
public function resolveDomainOwner(string $domain, int $whmcsClientId): ?int
{
$customerId = $this->resolve($whmcsClientId);
if ($customerId !== null) {
return $customerId;
}
$name = mb_strtolower(trim($domain));
$whmcs = DB::connection('whmcs');
$holderClientId = $whmcs->table('tbldomains as d')
->join('tblclients as c', 'c.id', '=', 'd.userid')
->whereRaw('LOWER(TRIM(d.domain)) = ?', [$name])
->orderByRaw("d.status = 'Active' DESC")
->orderByDesc('d.id')
->value('d.userid');
if ($holderClientId && ($customerId = $this->resolve((int) $holderClientId)) !== null) {
return $this->traced($name, $whmcsClientId, $customerId, 'domain_cung_ten_khach_khac', "Tên miền cùng tên đang do khách WHMCS #{$holderClientId} giữ.");
}
$linkedCustomerId = DB::table('dns_domain_user as l')
->join('dns_manager_domain as d', 'd.id', '=', 'l.domain_id')
->whereRaw('LOWER(d.domain_name) = ?', [$name])
->orderBy('l.id')
->value('l.user_id');
if ($linkedCustomerId) {
return $this->traced($name, $whmcsClientId, (int) $linkedCustomerId, 'domain_da_gan_khach_trong_dns', 'Tên miền đã gắn khách trong DNS Manager của portal.');
}
$contactEmails = $whmcs->table('tblcontacts')->where('userid', $whmcsClientId)->pluck('email')->map(fn ($email) => strtolower((string) $email))->filter()->all();
if ($contactEmails !== [] && ($customerId = DB::table('customers')->whereIn(DB::raw('LOWER(email)'), $contactEmails)->value('id'))) {
return $this->traced($name, $whmcsClientId, (int) $customerId, 'domain_khop_email_lien_he', 'Khớp theo email liên hệ của khách đã xoá.');
}
$this->report->note(
'domain',
$name,
$whmcsClientId,
'khach_khong_ton_tai_trong_whmcs',
"Khách WHMCS #{$whmcsClientId} đã xoá, không suy ra được chủ. Email liên hệ còn lại: " . ($contactEmails ? implode(', ', $contactEmails) : 'không có'),
);
return null;
}
private function traced(string $domain, int $whmcsClientId, int $customerId, string $issue, string $detail): int
{
$this->report->note('domain', $domain, $whmcsClientId, $issue, "Khách WHMCS #{$whmcsClientId} đã xoá; chủ suy ra → khách portal #{$customerId}. {$detail}");
return $customerId;
}
public static function flush(): void
{
self::$map = null;
self::$known = null;
}
private function build(): array
{
$clients = DB::connection('whmcs')->table('tblclients')->get(['id', 'uuid', 'email']);
$byUuid = DB::table('customers')->pluck('id', 'uuid');
$byEmail = DB::table('customers')->whereNotNull('email')->pluck('id', DB::raw('LOWER(email)'));
$map = [];
self::$known = [];
foreach ($clients as $client) {
self::$known[$client->id] = true;
$customerId = $byUuid[$client->uuid] ?? $byEmail[strtolower((string) $client->email)] ?? null;
if ($customerId !== null) {
$map[$client->id] = $customerId;
}
}
return $map;
}
}