74 lines
4.0 KiB
PHP
74 lines
4.0 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Mne;
|
|
|
|
use Illuminate\Support\Facades\DB;
|
|
use Ramsey\Uuid\Uuid;
|
|
|
|
final class CompatibilityBackfill
|
|
{
|
|
/** @return array<string, int> */
|
|
public function run(int $batchSize = 100): array
|
|
{
|
|
$counts = [];
|
|
foreach (['identity' => ['users', 'identity_links', 'user_id'], 'sector' => ['sectors', 'sector_links', 'sector_id'],
|
|
'business' => ['existing_businesses', 'business_links', 'legacy_business_id']] as $domain => [$source, $target, $key]) {
|
|
// Start again on each invocation to detect edits behind a previous checkpoint.
|
|
$last = null;
|
|
$processed = 0;
|
|
do {
|
|
$batch = DB::transaction(function () use ($domain, $source, $target, $key, $last, $processed, $batchSize) {
|
|
DB::select('SELECT pg_advisory_xact_lock(290926,170004)');
|
|
$query = DB::table('public.'.$source)->orderBy('id')->limit($batchSize)->lockForUpdate();
|
|
if ($last !== null) {
|
|
$query->where('id', '>', $last);
|
|
}
|
|
$rows = $query->get();
|
|
$checksums = [];
|
|
foreach ($rows as $row) {
|
|
$checksum = hash('sha256', json_encode((array) $row, JSON_THROW_ON_ERROR));
|
|
$checksums[] = $checksum;
|
|
$stableId = (string) Uuid::uuid5(Uuid::NAMESPACE_URL, 'investmoz:'.$domain.':'.$row->id);
|
|
$data = ['id' => $stableId, $key => $row->id, 'source_system' => 'public.'.$source,
|
|
'source_checksum' => $checksum, 'status' => 'pending'];
|
|
if ($domain === 'sector') {
|
|
$data['canonical_sector_id'] = $stableId;
|
|
$data['source_code'] = $row->slug;
|
|
$data['status'] = $row->is_active && $row->deleted_at === null ? 'active' : 'pending';
|
|
}
|
|
$existing = DB::table('mne.'.$target)->where($key, $row->id)->first();
|
|
if (! $existing) {
|
|
DB::table('mne.'.$target)->insert($data);
|
|
} elseif ($existing->source_checksum !== $checksum) {
|
|
// Changed sources invalidate previous reconciliation; never activate by inference.
|
|
DB::table('mne.'.$target)->where($key, $row->id)->update(['source_checksum' => $checksum, 'status' => 'pending']);
|
|
}
|
|
if ($domain !== 'sector' && (! $existing || $existing->status !== 'active' || $existing->source_checksum !== $checksum)) {
|
|
DB::table('mne.compatibility_rejections')->upsert([[
|
|
'id' => $stableId, 'domain' => $domain, 'source_key' => (string) $row->id,
|
|
'code' => 'CANONICAL_MAPPING_REQUIRED', 'cause' => 'Explicit canonical mapping and reconciliation required',
|
|
'resolved_at' => null, 'resolution' => null,
|
|
]], ['domain', 'source_key', 'code'], ['cause', 'resolved_at', 'resolution']);
|
|
}
|
|
}
|
|
if ($rows->isNotEmpty() || $processed === 0) {
|
|
DB::table('mne.compatibility_checkpoints')->updateOrInsert(['domain' => $domain], [
|
|
'last_key' => $rows->isEmpty() ? $last : (string) $rows->last()->id,
|
|
'processed' => $processed + $rows->count(), 'checksum' => hash('sha256', implode('', $checksums)), 'updated_at' => now(),
|
|
]);
|
|
}
|
|
|
|
return $rows;
|
|
});
|
|
$processed += $batch->count();
|
|
if ($batch->isNotEmpty()) {
|
|
$last = $batch->last()->id;
|
|
}
|
|
} while ($batch->count() === $batchSize);
|
|
$counts[$domain] = $processed;
|
|
}
|
|
|
|
return $counts;
|
|
}
|
|
}
|