feat(mng): 게시판-메뉴 자동 연동 및 URL 패턴 변경

## 주요 변경사항

### 게시판-메뉴 자동 연동
- 게시판 생성 시 메뉴 자동 생성 (BoardService.createBoardFromTemplate)
- 게시판 삭제 시 연결 메뉴 함께 삭제 (Soft Delete 연동)
- 게시판 복원 시 메뉴 재생성
- 게시판 영구삭제 시 메뉴 영구삭제

### 게시판 메뉴 보호
- MenuService: 게시판 연동 메뉴 수동 수정/삭제 방지
- isBoardMenuUrl(), isBoardMenu(), validateNotBoardUrl() 헬퍼 추가
- 8개 CRUD 메서드에 검증 로직 적용

### URL 패턴 변경
- 시스템 게시판: /system-boards/{code} → /customer-center/{code}
- 테넌트 게시판: /boards/{code} (변경 없음)

### UI 개선
- 메뉴 목록에서 게시판 메뉴 "📋 게시판" 뱃지 표시
- 게시판 메뉴는 수정/삭제 버튼 숨김 (활성/숨김 토글만 허용)
- 삭제된 게시판 행 클릭 시 404 오류 수정

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-27 21:54:48 +09:00
parent b54a04d588
commit cd6cf9746a
10 changed files with 475 additions and 94 deletions

View File

@@ -11,6 +11,10 @@
class BoardService
{
public function __construct(
protected MenuService $menuService
) {}
/**
* 시스템 게시판 목록 조회 (페이지네이션)
*/
@@ -316,10 +320,12 @@ public function getBaseFields(): array
/**
* 템플릿 기반 게시판 생성
*
* @param bool $createMenu 메뉴 자동 생성 여부 (기본: true)
*/
public function createBoardFromTemplate(array $data, ?string $templateType = null, ?string $templateKey = null): Board
public function createBoardFromTemplate(array $data, ?string $templateType = null, ?string $templateKey = null, bool $createMenu = true): Board
{
return DB::transaction(function () use ($data, $templateType, $templateKey) {
return DB::transaction(function () use ($data, $templateType, $templateKey, $createMenu) {
// 템플릿 설정 적용
$template = null;
if ($templateType && $templateKey) {
@@ -362,6 +368,16 @@ public function createBoardFromTemplate(array $data, ?string $templateType = nul
}
}
// 메뉴 자동 생성
if ($createMenu) {
$this->menuService->createMenuForBoard([
'board_code' => $board->board_code,
'name' => $board->name,
'is_system' => $board->is_system,
'tenant_id' => $board->tenant_id,
]);
}
return $board->load('fields');
});
}
@@ -376,21 +392,15 @@ public function createBoardFromTemplate(array $data, ?string $templateType = nul
public function getAllBoards(array $filters = [], int $perPage = 15): LengthAwarePaginator
{
$query = Board::query()
->with('tenant:id,code,company_name')
->withCount('fields')
->withTrashed();
// 게시판 유형 필터 (시스템/테넌트)
if (isset($filters['board_scope'])) {
if ($filters['board_scope'] === 'system') {
$query->where('is_system', true);
} elseif ($filters['board_scope'] === 'tenant') {
$query->where('is_system', false);
}
}
// 테넌트 필터
// 테넌트 필터: 선택 없으면 시스템만, 선택하면 해당 테넌트
if (! empty($filters['tenant_id'])) {
$query->where('tenant_id', $filters['tenant_id']);
} else {
$query->where('is_system', true);
}
// 검색 필터
@@ -489,6 +499,7 @@ public function updateAnyBoard(int $id, array $data): bool
/**
* 게시판 삭제 (시스템/테넌트 공통, Soft Delete)
* - 연결된 메뉴도 함께 Soft Delete
*/
public function deleteAnyBoard(int $id): bool
{
@@ -497,11 +508,19 @@ public function deleteAnyBoard(int $id): bool
$board->deleted_by = auth()->id();
$board->save();
// 연결된 메뉴도 함께 삭제 (Soft Delete)
$this->menuService->deleteMenuForBoard(
$board->board_code,
$board->is_system,
$board->tenant_id
);
return $board->delete();
}
/**
* 게시판 복원 (시스템/테넌트 공통)
* - 연결된 메뉴도 함께 복원
*/
public function restoreAnyBoard(int $id): bool
{
@@ -509,11 +528,25 @@ public function restoreAnyBoard(int $id): bool
$board->deleted_by = null;
return $board->restore();
// 게시판 복원
$result = $board->restore();
// 메뉴가 없으면 다시 생성
if ($result) {
$this->menuService->createMenuForBoard([
'board_code' => $board->board_code,
'name' => $board->name,
'is_system' => $board->is_system,
'tenant_id' => $board->tenant_id,
]);
}
return $result;
}
/**
* 게시판 영구 삭제 (시스템/테넌트 공통)
* - 연결된 메뉴도 함께 영구 삭제
*/
public function forceDeleteAnyBoard(int $id): bool
{
@@ -522,6 +555,14 @@ public function forceDeleteAnyBoard(int $id): bool
// 관련 필드 삭제
$board->fields()->delete();
// 연결된 메뉴도 함께 영구 삭제
$this->menuService->deleteMenuForBoard(
$board->board_code,
$board->is_system,
$board->tenant_id,
true // forceDelete
);
return $board->forceDelete();
}
}