feat: Phase 5.1-1 사용자 초대 + Phase 5.2 알림 설정 API 연동

- 사용자 초대 API: role 문자열 지원 추가 (React 호환)
- 알림 설정 API: 그룹 기반 계층 구조 구현
  - notification_setting_groups 테이블 추가
  - notification_setting_group_items 테이블 추가
  - notification_setting_group_states 테이블 추가
  - GET/PUT /api/v1/settings/notifications 엔드포인트 추가
- Pint 코드 스타일 정리
This commit is contained in:
2025-12-22 17:42:59 +09:00
parent eeca8d3e0f
commit a27b1b2091
43 changed files with 2980 additions and 144 deletions

View File

@@ -0,0 +1,64 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
// 알림 그룹 정의 (테넌트별 커스터마이징 가능)
Schema::create('notification_setting_groups', function (Blueprint $table) {
$table->id();
$table->foreignId('tenant_id')->constrained()->cascadeOnDelete()->comment('테넌트 ID');
$table->string('code', 50)->comment('그룹 코드 (notice, schedule, vendor 등)');
$table->string('name', 100)->comment('그룹명 (공지 알림, 일정 알림 등)');
$table->unsignedSmallInteger('sort_order')->default(0)->comment('정렬 순서');
$table->boolean('is_active')->default(true)->comment('활성화 여부');
$table->timestamps();
$table->unique(['tenant_id', 'code']);
$table->index(['tenant_id', 'is_active', 'sort_order']);
});
// 그룹-타입 매핑 (어떤 알림 타입이 어떤 그룹에 속하는지)
Schema::create('notification_setting_group_items', function (Blueprint $table) {
$table->id();
$table->foreignId('group_id')->constrained('notification_setting_groups')->cascadeOnDelete()->comment('그룹 ID');
$table->string('notification_type', 50)->comment('알림 타입 (notice, event, vat_report 등)');
$table->string('label', 100)->comment('항목 라벨 (공지사항 알림, 이벤트 알림 등)');
$table->unsignedSmallInteger('sort_order')->default(0)->comment('정렬 순서');
$table->timestamps();
$table->unique(['group_id', 'notification_type'], 'noti_grp_items_group_type_unique');
$table->index(['group_id', 'sort_order'], 'noti_grp_items_group_sort_idx');
});
// 사용자별 그룹 활성화 상태
Schema::create('notification_setting_group_states', function (Blueprint $table) {
$table->id();
$table->foreignId('tenant_id')->constrained()->cascadeOnDelete()->comment('테넌트 ID');
$table->foreignId('user_id')->constrained()->cascadeOnDelete()->comment('사용자 ID');
$table->string('group_code', 50)->comment('그룹 코드');
$table->boolean('enabled')->default(true)->comment('그룹 전체 활성화 여부');
$table->timestamps();
$table->unique(['tenant_id', 'user_id', 'group_code'], 'noti_grp_states_tenant_user_group_unique');
$table->index(['tenant_id', 'user_id'], 'noti_grp_states_tenant_user_idx');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('notification_setting_group_states');
Schema::dropIfExists('notification_setting_group_items');
Schema::dropIfExists('notification_setting_groups');
}
};

View File

@@ -194,4 +194,4 @@ public function run(): void
$this->command->info('✅ React 메뉴 생성 완료 (global_menus 테이블, 11개 대메뉴, 54개 중메뉴)');
}
}
}

View File

@@ -16,7 +16,7 @@ public function run(): void
// ========================================
// items 테이블 시스템 필드
// ========================================
...array_map(fn($field) => array_merge($field, [
...array_map(fn ($field) => array_merge($field, [
'source_table' => 'items',
'source_table_label' => '품목',
]), [
@@ -36,7 +36,7 @@ public function run(): void
// ========================================
// tenants 테이블 시스템 필드
// ========================================
...array_map(fn($field) => array_merge($field, [
...array_map(fn ($field) => array_merge($field, [
'source_table' => 'tenants',
'source_table_label' => '테넌트',
]), [
@@ -57,7 +57,7 @@ public function run(): void
// ========================================
// users 테이블 시스템 필드
// ========================================
...array_map(fn($field) => array_merge($field, [
...array_map(fn ($field) => array_merge($field, [
'source_table' => 'users',
'source_table_label' => '사용자',
]), [
@@ -80,6 +80,6 @@ public function run(): void
);
}
$this->command->info('SystemFieldDefinition 시딩 완료: ' . count($definitions) . '개');
$this->command->info('SystemFieldDefinition 시딩 완료: '.count($definitions).'개');
}
}