diff --git a/app/Http/Controllers/Api/Admin/ArchivedRecordController.php b/app/Http/Controllers/Api/Admin/ArchivedRecordController.php index 7996869a..ecd9190e 100644 --- a/app/Http/Controllers/Api/Admin/ArchivedRecordController.php +++ b/app/Http/Controllers/Api/Admin/ArchivedRecordController.php @@ -14,11 +14,11 @@ public function __construct( ) {} /** - * 아카이브 레코드 목록 조회 + * 아카이브 레코드 목록 조회 (batch 그룹핑) */ public function index(Request $request): JsonResponse { - $records = $this->archivedRecordService->getArchivedRecords( + $records = $this->archivedRecordService->getArchivedRecordsBatched( $request->all(), $request->integer('per_page', 15) ); @@ -46,13 +46,13 @@ public function index(Request $request): JsonResponse } /** - * 특정 아카이브 레코드 조회 + * 특정 batch의 레코드 조회 */ - public function show(Request $request, int $id): JsonResponse + public function show(Request $request, string $batchId): JsonResponse { - $record = $this->archivedRecordService->getArchivedRecordById($id); + $records = $this->archivedRecordService->getRecordsByBatchId($batchId); - if (! $record) { + if ($records->isEmpty()) { return response()->json([ 'success' => false, 'message' => '아카이브 레코드를 찾을 수 없습니다.', @@ -62,13 +62,13 @@ public function show(Request $request, int $id): JsonResponse // HTMX 요청 시 HTML 반환 if ($request->header('HX-Request')) { return response()->json([ - 'html' => view('archived-records.partials.detail', compact('record'))->render(), + 'html' => view('archived-records.partials.detail', compact('records'))->render(), ]); } return response()->json([ 'success' => true, - 'data' => $record, + 'data' => $records, ]); } diff --git a/app/Http/Controllers/ArchivedRecordController.php b/app/Http/Controllers/ArchivedRecordController.php index bb4be804..8c62f68b 100644 --- a/app/Http/Controllers/ArchivedRecordController.php +++ b/app/Http/Controllers/ArchivedRecordController.php @@ -22,16 +22,25 @@ public function index(): View } /** - * 아카이브 레코드 상세 보기 (Blade 화면만) + * 아카이브 레코드 상세 보기 (batch 기준) */ - public function show(int $id): View + public function show(string $batchId): View { - $record = $this->archivedRecordService->getArchivedRecordById($id); + $records = $this->archivedRecordService->getRecordsByBatchId($batchId); - if (! $record) { + if ($records->isEmpty()) { abort(404, '아카이브 레코드를 찾을 수 없습니다.'); } - return view('archived-records.show', compact('record')); + // 첫 번째 레코드에서 batch 정보 추출 + $batchInfo = [ + 'batch_id' => $batchId, + 'batch_description' => $records->first()->batch_description, + 'deleted_by' => $records->first()->deletedByUser?->name ?? '-', + 'deleted_at' => $records->first()->deleted_at, + 'record_count' => $records->count(), + ]; + + return view('archived-records.show', compact('records', 'batchInfo')); } } diff --git a/app/Models/Archives/ArchivedRecord.php b/app/Models/Archives/ArchivedRecord.php index 9f908f0b..280ec754 100644 --- a/app/Models/Archives/ArchivedRecord.php +++ b/app/Models/Archives/ArchivedRecord.php @@ -12,6 +12,8 @@ class ArchivedRecord extends Model protected $table = 'archived_records'; protected $fillable = [ + 'batch_id', + 'batch_description', 'record_type', 'original_id', 'main_data', diff --git a/app/Services/ArchivedRecordService.php b/app/Services/ArchivedRecordService.php index 5f3cf940..79f115a0 100644 --- a/app/Services/ArchivedRecordService.php +++ b/app/Services/ArchivedRecordService.php @@ -4,48 +4,43 @@ use App\Models\Archives\ArchivedRecord; use Illuminate\Contracts\Pagination\LengthAwarePaginator; +use Illuminate\Support\Collection; +use Illuminate\Support\Facades\DB; class ArchivedRecordService { /** - * 아카이브 레코드 목록 조회 (페이지네이션) + * 아카이브 레코드 목록 조회 (batch_id로 그룹핑, 페이지네이션) */ - public function getArchivedRecords(array $filters = [], int $perPage = 15): LengthAwarePaginator + public function getArchivedRecordsBatched(array $filters = [], int $perPage = 15): LengthAwarePaginator { + // batch_id별로 그룹핑하여 첫 번째 레코드만 조회 $query = ArchivedRecord::query() - ->with(['deletedByUser', 'relations']) - ->withCount('relations'); + ->select([ + 'batch_id', + 'batch_description', + DB::raw('MIN(id) as id'), + DB::raw('GROUP_CONCAT(DISTINCT record_type) as record_types'), + DB::raw('COUNT(*) as record_count'), + DB::raw('MIN(deleted_by) as deleted_by'), + DB::raw('MIN(deleted_at) as deleted_at'), + ]) + ->groupBy('batch_id', 'batch_description'); // 레코드 타입 필터 if (! empty($filters['record_type'])) { - $query->where('record_type', $filters['record_type']); + $query->having('record_types', 'like', "%{$filters['record_type']}%"); } // 삭제자 필터 if (! empty($filters['deleted_by'])) { - $query->where('deleted_by', $filters['deleted_by']); - } - - // 노트 유무 필터 - if (isset($filters['has_notes'])) { - if ($filters['has_notes'] === 'yes' || $filters['has_notes'] === '1') { - $query->whereNotNull('notes')->where('notes', '!=', ''); - } elseif ($filters['has_notes'] === 'no' || $filters['has_notes'] === '0') { - $query->where(function ($q) { - $q->whereNull('notes')->orWhere('notes', ''); - }); - } + $query->having('deleted_by', $filters['deleted_by']); } // 검색 if (! empty($filters['search'])) { $search = $filters['search']; - $query->where(function ($q) use ($search) { - $q->where('record_type', 'like', "%{$search}%") - ->orWhere('notes', 'like', "%{$search}%") - ->orWhereRaw("JSON_EXTRACT(main_data, '$.name') LIKE ?", ["%{$search}%"]) - ->orWhereRaw("JSON_EXTRACT(main_data, '$.title') LIKE ?", ["%{$search}%"]); - }); + $query->where('batch_description', 'like', "%{$search}%"); } // 정렬 (기본: 삭제일시 내림차순) @@ -56,6 +51,17 @@ public function getArchivedRecords(array $filters = [], int $perPage = 15): Leng return $query->paginate($perPage); } + /** + * 특정 batch의 모든 레코드 조회 + */ + public function getRecordsByBatchId(string $batchId): Collection + { + return ArchivedRecord::with(['deletedByUser', 'relations']) + ->where('batch_id', $batchId) + ->orderBy('id') + ->get(); + } + /** * 특정 아카이브 레코드 조회 */ @@ -65,6 +71,17 @@ public function getArchivedRecordById(int $id): ?ArchivedRecord ->find($id); } + /** + * batch_id로 첫 번째 레코드 조회 (상세 페이지 진입용) + */ + public function getFirstRecordByBatchId(string $batchId): ?ArchivedRecord + { + return ArchivedRecord::with(['deletedByUser', 'relations']) + ->where('batch_id', $batchId) + ->orderBy('id') + ->first(); + } + /** * 삭제자 목록 조회 (필터용) */ @@ -87,12 +104,10 @@ public function getDeletedByUsers(): array public function getStats(): array { return [ - 'total' => ArchivedRecord::count(), + 'total_batches' => ArchivedRecord::distinct('batch_id')->count('batch_id'), + 'total_records' => ArchivedRecord::count(), 'tenants' => ArchivedRecord::tenant()->count(), 'users' => ArchivedRecord::user()->count(), - 'with_notes' => ArchivedRecord::whereNotNull('notes') - ->where('notes', '!=', '') - ->count(), ]; } } diff --git a/resources/views/archived-records/index.blade.php b/resources/views/archived-records/index.blade.php index 8a32f2ae..0636faf9 100644 --- a/resources/views/archived-records/index.blade.php +++ b/resources/views/archived-records/index.blade.php @@ -46,7 +46,7 @@ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none foc
diff --git a/resources/views/archived-records/partials/table.blade.php b/resources/views/archived-records/partials/table.blade.php index f63d5426..49483942 100644 --- a/resources/views/archived-records/partials/table.blade.php +++ b/resources/views/archived-records/partials/table.blade.php @@ -2,64 +2,53 @@ - - - - + + + - - @forelse($records as $record) - - - - + - - @empty -
ID타입원본 ID데이터 요약작업 설명레코드 타입레코드 수 삭제자 삭제일시관련 테이블노트 작업
- {{ $record->id }} + +
{{ $record->batch_description ?? '삭제 작업' }}
+
ID: {{ Str::limit($record->batch_id, 8, '...') }}
- - {{ $record->record_type_label }} - - - {{ $record->original_id }} - -
- {{ $record->main_data_summary }} + @php + $types = explode(',', $record->record_types ?? ''); + @endphp +
+ @foreach($types as $type) + @if($type === 'tenant') + 테넌트 + @elseif($type === 'user') + 사용자 + @else + {{ $type }} + @endif + @endforeach
- {{ $record->deletedByUser?->name ?? '-' }} + + + {{ $record->record_count }}건 + - {{ $record->deleted_at?->format('Y-m-d H:i') ?? '-' }} + @php + $deletedByUser = $record->deleted_by ? \App\Models\User::find($record->deleted_by) : null; + @endphp + {{ $deletedByUser?->name ?? '-' }} + + {{ $record->deleted_at ? \Carbon\Carbon::parse($record->deleted_at)->format('Y-m-d H:i') : '-' }} - @if($record->relations_count > 0) - - {{ $record->relations_count }} - - @else - - - @endif - - @if($record->notes) - - - - - - @else - - - @endif - - @@ -71,7 +60,7 @@ class="text-blue-600 hover:text-blue-900 transition"
+
@@ -90,7 +79,7 @@ class="text-blue-600 hover:text-blue-900 transition"
- 총 {{ $records->total() }}개 중 + 총 {{ $records->total() }}개 작업 중 {{ $records->firstItem() }} - {{ $records->lastItem() }}개 표시
diff --git a/resources/views/archived-records/show.blade.php b/resources/views/archived-records/show.blade.php index 988c5c5d..26755a52 100644 --- a/resources/views/archived-records/show.blade.php +++ b/resources/views/archived-records/show.blade.php @@ -13,100 +13,131 @@ class="text-gray-500 hover:text-gray-700 transition">

삭제된 데이터 상세

- - {{ $record->record_type_label }} -
- +
-

기본 정보

+

작업 요약

-
+
-
ID
-
{{ $record->id }}
+
작업 설명
+
{{ $batchInfo['batch_description'] ?? '삭제 작업' }}
-
레코드 타입
+
삭제된 레코드 수
- - {{ $record->record_type_label }} + + {{ $batchInfo['record_count'] }}건
-
-
원본 ID
-
{{ $record->original_id }}
-
-
-
스키마 버전
-
{{ $record->schema_version ?? '-' }}
-
삭제자
-
{{ $record->deletedByUser?->name ?? '-' }}
+
{{ $batchInfo['deleted_by'] }}
삭제일시
-
{{ $record->deleted_at?->format('Y-m-d H:i:s') ?? '-' }}
+
{{ $batchInfo['deleted_at']?->format('Y-m-d H:i:s') ?? '-' }}
- @if($record->notes) -
-
노트
-
{{ $record->notes }}
-
- @endif
- + + @foreach($records as $index => $record)
-
-

메인 데이터

+
+
+ + {{ $index + 1 }} + +

{{ $record->record_type_label }}

+ + {{ $record->record_type }} + +
+ 원본 ID: {{ $record->original_id }}
-
+ + +
+

메인 데이터

@if($record->main_data) -
{{ json_encode($record->main_data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) }}
+ @php + $mainData = is_array($record->main_data) ? $record->main_data : json_decode($record->main_data, true); + @endphp +
+ @foreach(array_slice($mainData ?? [], 0, 8) as $key => $value) +
+
{{ $key }}
+
+ @if(is_array($value)) + [배열] + @elseif(is_null($value)) + null + @else + {{ Str::limit((string)$value, 50) }} + @endif +
+
+ @endforeach +
+
+ 전체 데이터 보기 +
{{ json_encode($mainData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) }}
+
@else -

데이터가 없습니다.

+

메인 데이터가 없습니다.

@endif
-
- - @if($record->relations->isNotEmpty()) -
-
-

관련 테이블 데이터

-

총 {{ $record->relations->count() }}개 테이블

-
-
- @foreach($record->relations as $relation) -
-
-
- {{ $relation->table_name_label }} - ({{ $relation->table_name }}) + + @if($record->relations->isNotEmpty()) +
+

관련 테이블 데이터 ({{ $record->relations->count() }}개)

+
+ @foreach($record->relations as $relation) +
+ +
+ {{ $relation->table_name_label }} + ({{ $relation->table_name }}) +
+ + {{ $relation->record_count }}건 + +
+
+ @if($relation->data) +
{{ json_encode(is_array($relation->data) ? $relation->data : json_decode($relation->data, true), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) }}
+ @else +

데이터가 없습니다.

+ @endif
- - {{ $relation->record_count }} 건 - -
-
- @if($relation->data) -
{{ json_encode($relation->data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) }}
- @else -

데이터가 없습니다.

- @endif + + @endforeach +
+
+ @endif + + + @if($record->notes) +
+
+ + + +
+ 노트 +

{{ $record->notes }}

- @endforeach
+ @endif
- @endif + @endforeach @endsection diff --git a/routes/web.php b/routes/web.php index 03e7c4b6..d661a1a4 100644 --- a/routes/web.php +++ b/routes/web.php @@ -91,7 +91,7 @@ // 삭제된 데이터 백업 (Blade 화면만) Route::prefix('archived-records')->name('archived-records.')->group(function () { Route::get('/', [ArchivedRecordController::class, 'index'])->name('index'); - Route::get('/{id}', [ArchivedRecordController::class, 'show'])->name('show'); + Route::get('/{batchId}', [ArchivedRecordController::class, 'show'])->name('show'); }); // 대시보드