feat: [esign] 전자서명 고도화 - 필기 검증 템플릿·검증 결과 마이그레이션 및 모델 추가

This commit is contained in:
김보곤
2026-03-22 22:26:23 +09:00
parent 404b61c426
commit d5bb98e953
4 changed files with 199 additions and 0 deletions

View File

@@ -0,0 +1,62 @@
<?php
namespace App\Models\ESign;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class EsignHandwritingVerification extends Model
{
protected $table = 'esign_handwriting_verifications';
protected $fillable = [
'tenant_id',
'contract_id',
'signer_id',
'step_order',
'prompt_text',
'recognized_text',
'similarity_score',
'is_passed',
'handwriting_image',
'hwr_engine',
'hwr_confidence',
'hwr_raw_response',
'attempt_number',
'verified_at',
'ip_address',
'user_agent',
'options',
];
protected $casts = [
'step_order' => 'integer',
'similarity_score' => 'decimal:2',
'is_passed' => 'boolean',
'hwr_confidence' => 'decimal:2',
'hwr_raw_response' => 'array',
'attempt_number' => 'integer',
'verified_at' => 'datetime',
'options' => 'array',
];
public function contract(): BelongsTo
{
return $this->belongsTo(EsignContract::class, 'contract_id');
}
public function signer(): BelongsTo
{
return $this->belongsTo(EsignSigner::class, 'signer_id');
}
public function scopeForTenant($query, $tenantId)
{
return $query->where('tenant_id', $tenantId);
}
public function scopePassed($query)
{
return $query->where('is_passed', true);
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace App\Models\ESign;
use Illuminate\Database\Eloquent\Model;
class EsignVerificationTemplate extends Model
{
protected $table = 'esign_verification_templates';
protected $fillable = [
'tenant_id',
'name',
'category',
'steps',
'pass_threshold',
'max_attempts',
'is_active',
'created_by',
'options',
];
protected $casts = [
'steps' => 'array',
'pass_threshold' => 'decimal:2',
'max_attempts' => 'integer',
'is_active' => 'boolean',
'options' => 'array',
];
public function scopeForTenant($query, $tenantId)
{
return $query->where('tenant_id', $tenantId);
}
public function scopeActive($query)
{
return $query->where('is_active', true);
}
public function getOption(string $key, mixed $default = null): mixed
{
$options = $this->options ?? [];
return $options[$key] ?? $default;
}
public function setOption(string $key, mixed $value): void
{
$options = $this->options ?? [];
$options[$key] = $value;
$this->options = $options;
}
}

View File

@@ -0,0 +1,32 @@
<?php
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::create('esign_verification_templates', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('tenant_id');
$table->string('name', 100);
$table->string('category', 50)->nullable();
$table->json('steps'); // [{ "order": 1, "text": "본인은 위 내용을 확인하였습니다", "threshold": 80 }]
$table->decimal('pass_threshold', 5, 2)->default(80.00);
$table->unsignedTinyInteger('max_attempts')->default(5);
$table->boolean('is_active')->default(true);
$table->unsignedBigInteger('created_by')->nullable();
$table->json('options')->nullable();
$table->timestamps();
$table->index(['tenant_id', 'is_active'], 'idx_tenant_active');
});
}
public function down(): void
{
Schema::dropIfExists('esign_verification_templates');
}
};

View File

@@ -0,0 +1,51 @@
<?php
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::create('esign_handwriting_verifications', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('tenant_id');
$table->unsignedBigInteger('contract_id');
$table->unsignedBigInteger('signer_id');
$table->unsignedTinyInteger('step_order')->default(1);
$table->string('prompt_text', 200);
$table->string('recognized_text', 500)->nullable();
$table->decimal('similarity_score', 5, 2)->nullable();
$table->boolean('is_passed')->default(false);
$table->string('handwriting_image', 500)->nullable();
$table->string('hwr_engine', 50)->nullable();
$table->decimal('hwr_confidence', 5, 2)->nullable();
$table->json('hwr_raw_response')->nullable();
$table->unsignedTinyInteger('attempt_number')->default(1);
$table->timestamp('verified_at')->nullable();
$table->string('ip_address', 45)->nullable();
$table->string('user_agent', 500)->nullable();
$table->json('options')->nullable();
$table->timestamps();
$table->index(['contract_id', 'signer_id'], 'idx_contract_signer');
$table->index('tenant_id', 'idx_tenant');
$table->foreign('contract_id')
->references('id')
->on('esign_contracts')
->onDelete('cascade');
$table->foreign('signer_id')
->references('id')
->on('esign_signers')
->onDelete('cascade');
});
}
public function down(): void
{
Schema::dropIfExists('esign_handwriting_verifications');
}
};