feat: 글로벌 메뉴 분리 및 테넌트 메뉴 동기화 시스템 구현

- global_menus 테이블 분리를 위한 menus 컬럼 추가 (global_menu_id, is_customized)
- GlobalMenuController: 글로벌 메뉴 CRUD API
- GlobalMenuService: 글로벌 메뉴 비즈니스 로직
- MenuSyncService: 테넌트 메뉴 동기화 서비스
- MenuBootstrapService: 테넌트 초기 메뉴 생성 로직 개선
- MenuController: 메뉴 재동기화 엔드포인트 추가
This commit is contained in:
2025-12-02 22:11:08 +09:00
parent 84eb4f5ab4
commit a72a744612
8 changed files with 1140 additions and 4 deletions

View File

@@ -112,6 +112,7 @@ public static function store(array $params)
/**
* 메뉴 수정
* - global_menu_id가 있는 테넌트 메뉴 수정 시 is_customized = true 자동 설정
*/
public static function update(array $params)
{
@@ -154,6 +155,11 @@ public static function update(array $params)
return ['error' => '수정할 데이터가 없습니다.', 'code' => 400];
}
// 글로벌 메뉴에서 복제된 테넌트 메뉴 수정 시 커스터마이징 플래그 설정
if ($menu->global_menu_id && ! $menu->is_customized) {
$update['is_customized'] = true;
}
$update['updated_by'] = $userId;
$menu->fill($update)->save();
@@ -246,4 +252,60 @@ public static function toggle(array $params)
return $menu->fresh();
}
/**
* 삭제된 메뉴 복원
*/
public static function restore(array $params)
{
$id = (int) ($params['id'] ?? 0);
$tenantId = self::tenantId();
$userId = self::actorId();
if (! $id) {
return ['error' => 'id가 필요합니다.', 'code' => 400];
}
// 삭제된 메뉴 포함하여 조회
$menu = Menu::withTrashed()
->withoutGlobalScopes()
->where(function ($q) use ($tenantId) {
$q->whereNull('tenant_id')
->orWhere('tenant_id', $tenantId);
})
->where('id', $id)
->first();
if (! $menu) {
return ['error' => 'Menu not found', 'code' => 404];
}
if (! $menu->trashed()) {
return ['error' => '삭제되지 않은 메뉴입니다.', 'code' => 400];
}
$menu->restore();
$menu->deleted_by = null;
$menu->updated_by = $userId;
$menu->save();
return $menu->fresh();
}
/**
* 삭제된 메뉴 목록 조회
*/
public static function trashedList(array $params = [])
{
$tenantId = self::tenantId();
return Menu::onlyTrashed()
->withoutGlobalScopes()
->where(function ($q) use ($tenantId) {
$q->whereNull('tenant_id')
->orWhere('tenant_id', $tenantId);
})
->orderBy('deleted_at', 'desc')
->get();
}
}