feat: complete person identity matching system with fuzzy detection, revert, and manual link
Implements the full identity matching engine: email matching (HIGH confidence), fuzzy name matching with Levenshtein distance (MEDIUM confidence, upgradable to HIGH with DOB tiebreaker), manual link/unlink, revert confirmed matches, and automatic detection via PersonObserver. Includes 33 comprehensive tests, frontend integration with confirm/dismiss/unlink UI, and match indicators in the persons list. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->date('date_of_birth')->nullable()->after('last_name');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('date_of_birth');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('person_identity_matches', function (Blueprint $table) {
|
||||
$table->json('match_details')->nullable()->after('status');
|
||||
|
||||
$table->foreignUlid('confirmed_by_user_id')
|
||||
->nullable()
|
||||
->after('match_details')
|
||||
->constrained('users')
|
||||
->nullOnDelete();
|
||||
|
||||
$table->timestamp('confirmed_at')->nullable()->after('confirmed_by_user_id');
|
||||
|
||||
$table->foreignUlid('dismissed_by_user_id')
|
||||
->nullable()
|
||||
->after('confirmed_at')
|
||||
->constrained('users')
|
||||
->nullOnDelete();
|
||||
|
||||
$table->timestamp('dismissed_at')->nullable()->after('dismissed_by_user_id');
|
||||
|
||||
$table->foreignUlid('reverted_by_user_id')
|
||||
->nullable()
|
||||
->after('dismissed_at')
|
||||
->constrained('users')
|
||||
->nullOnDelete();
|
||||
|
||||
$table->timestamp('reverted_at')->nullable()->after('reverted_by_user_id');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('person_identity_matches', function (Blueprint $table) {
|
||||
$table->dropForeign(['confirmed_by_user_id']);
|
||||
$table->dropForeign(['dismissed_by_user_id']);
|
||||
$table->dropForeign(['reverted_by_user_id']);
|
||||
$table->dropColumn([
|
||||
'match_details',
|
||||
'confirmed_by_user_id',
|
||||
'confirmed_at',
|
||||
'dismissed_by_user_id',
|
||||
'dismissed_at',
|
||||
'reverted_by_user_id',
|
||||
'reverted_at',
|
||||
]);
|
||||
});
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user