feat: person identity matching with detection, confirmation and audit trail

Implements enterprise-grade identity resolution (detect → suggest → confirm)
for Person ↔ User linking. Matches are detected automatically on person
creation and user account creation, then surfaced to organisers for explicit
confirmation or dismissal. No silent auto-linking.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-10 12:50:25 +02:00
parent 239fe57a11
commit 4b182b449a
20 changed files with 1463 additions and 2 deletions

View File

@@ -15,6 +15,7 @@ use App\Http\Controllers\Api\V1\MeController;
use App\Http\Controllers\Api\V1\MemberController;
use App\Http\Controllers\Api\V1\OrganisationController;
use App\Http\Controllers\Api\V1\PersonController;
use App\Http\Controllers\Api\V1\PersonIdentityMatchController;
use App\Http\Controllers\Api\V1\PersonTagController;
use App\Http\Controllers\Api\V1\ShiftController;
use App\Http\Controllers\Api\V1\TimeSlotController;
@@ -94,6 +95,13 @@ Route::middleware('auth:sanctum')->group(function () {
Route::put('users/{user}/tags/sync', [UserOrganisationTagController::class, 'sync']);
Route::delete('users/{user}/tags/{userOrganisationTag}', [UserOrganisationTagController::class, 'destroy']);
// Identity matches
Route::get('identity-matches', [PersonIdentityMatchController::class, 'index']);
Route::get('persons/{person}/identity-match', [PersonIdentityMatchController::class, 'showForPerson']);
Route::post('identity-matches/{personIdentityMatch}/confirm', [PersonIdentityMatchController::class, 'confirm']);
Route::post('identity-matches/{personIdentityMatch}/dismiss', [PersonIdentityMatchController::class, 'dismiss']);
Route::post('identity-matches/bulk-confirm', [PersonIdentityMatchController::class, 'bulkConfirm']);
// Invitations & Members
Route::post('invite', [InvitationController::class, 'invite']);
Route::delete('invitations/{invitation}', [InvitationController::class, 'revoke']);