feat: 문서 관리 시스템 MNG 관리자 패널 구현 (Phase 2)
- Document 관련 모델 4개 생성 (Document, DocumentApproval, DocumentData, DocumentAttachment) - DocumentController 생성 (목록/생성/상세/수정 페이지) - DocumentApiController 생성 (AJAX CRUD 처리) - 문서 관리 뷰 3개 생성 (index, edit, show) - 웹/API 라우트 등록 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
226
app/Http/Controllers/Api/Admin/DocumentApiController.php
Normal file
226
app/Http/Controllers/Api/Admin/DocumentApiController.php
Normal file
@@ -0,0 +1,226 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Documents\Document;
|
||||
use App\Models\Documents\DocumentData;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class DocumentApiController extends Controller
|
||||
{
|
||||
/**
|
||||
* 문서 목록 조회
|
||||
*/
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
$tenantId = session('selected_tenant_id');
|
||||
|
||||
$query = Document::with(['template', 'creator'])
|
||||
->where('tenant_id', $tenantId)
|
||||
->orderBy('created_at', 'desc');
|
||||
|
||||
// 상태 필터
|
||||
if ($request->filled('status')) {
|
||||
$query->where('status', $request->status);
|
||||
}
|
||||
|
||||
// 템플릿 필터
|
||||
if ($request->filled('template_id')) {
|
||||
$query->where('template_id', $request->template_id);
|
||||
}
|
||||
|
||||
// 검색
|
||||
if ($request->filled('search')) {
|
||||
$search = $request->search;
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->where('document_no', 'like', "%{$search}%")
|
||||
->orWhere('title', 'like', "%{$search}%");
|
||||
});
|
||||
}
|
||||
|
||||
$documents = $query->paginate($request->input('per_page', 15));
|
||||
|
||||
return response()->json($documents);
|
||||
}
|
||||
|
||||
/**
|
||||
* 문서 상세 조회
|
||||
*/
|
||||
public function show(int $id): JsonResponse
|
||||
{
|
||||
$tenantId = session('selected_tenant_id');
|
||||
|
||||
$document = Document::with([
|
||||
'template.approvalLines',
|
||||
'template.basicFields',
|
||||
'template.sections.items',
|
||||
'template.columns',
|
||||
'approvals.user',
|
||||
'data',
|
||||
'attachments.file',
|
||||
'creator',
|
||||
'updater',
|
||||
])->where('tenant_id', $tenantId)->findOrFail($id);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'data' => $document,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 문서 생성
|
||||
*/
|
||||
public function store(Request $request): JsonResponse
|
||||
{
|
||||
$tenantId = session('selected_tenant_id');
|
||||
$userId = auth()->id();
|
||||
|
||||
$request->validate([
|
||||
'template_id' => 'required|exists:document_templates,id',
|
||||
'title' => 'required|string|max:255',
|
||||
'data' => 'nullable|array',
|
||||
'data.*.field_key' => 'required|string',
|
||||
'data.*.field_value' => 'nullable|string',
|
||||
]);
|
||||
|
||||
// 문서 번호 생성
|
||||
$documentNo = $this->generateDocumentNo($tenantId, $request->template_id);
|
||||
|
||||
$document = Document::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'template_id' => $request->template_id,
|
||||
'document_no' => $documentNo,
|
||||
'title' => $request->title,
|
||||
'status' => Document::STATUS_DRAFT,
|
||||
'created_by' => $userId,
|
||||
'updated_by' => $userId,
|
||||
]);
|
||||
|
||||
// 문서 데이터 저장
|
||||
if ($request->filled('data')) {
|
||||
foreach ($request->data as $item) {
|
||||
if (! empty($item['field_value'])) {
|
||||
DocumentData::create([
|
||||
'document_id' => $document->id,
|
||||
'field_key' => $item['field_key'],
|
||||
'field_value' => $item['field_value'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => '문서가 저장되었습니다.',
|
||||
'data' => $document->fresh(['template', 'data']),
|
||||
], 201);
|
||||
}
|
||||
|
||||
/**
|
||||
* 문서 수정
|
||||
*/
|
||||
public function update(int $id, Request $request): JsonResponse
|
||||
{
|
||||
$tenantId = session('selected_tenant_id');
|
||||
$userId = auth()->id();
|
||||
|
||||
$document = Document::where('tenant_id', $tenantId)->findOrFail($id);
|
||||
|
||||
// 작성중 또는 반려 상태에서만 수정 가능
|
||||
if (! in_array($document->status, [Document::STATUS_DRAFT, Document::STATUS_REJECTED])) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => '현재 상태에서는 수정할 수 없습니다.',
|
||||
], 422);
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'title' => 'sometimes|required|string|max:255',
|
||||
'data' => 'nullable|array',
|
||||
'data.*.field_key' => 'required|string',
|
||||
'data.*.field_value' => 'nullable|string',
|
||||
]);
|
||||
|
||||
$document->update([
|
||||
'title' => $request->input('title', $document->title),
|
||||
'updated_by' => $userId,
|
||||
]);
|
||||
|
||||
// 문서 데이터 업데이트
|
||||
if ($request->has('data')) {
|
||||
// 기존 데이터 삭제
|
||||
$document->data()->delete();
|
||||
|
||||
// 새 데이터 저장
|
||||
foreach ($request->data as $item) {
|
||||
if (! empty($item['field_value'])) {
|
||||
DocumentData::create([
|
||||
'document_id' => $document->id,
|
||||
'field_key' => $item['field_key'],
|
||||
'field_value' => $item['field_value'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => '문서가 수정되었습니다.',
|
||||
'data' => $document->fresh(['template', 'data']),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 문서 삭제 (소프트 삭제)
|
||||
*/
|
||||
public function destroy(int $id): JsonResponse
|
||||
{
|
||||
$tenantId = session('selected_tenant_id');
|
||||
|
||||
$document = Document::where('tenant_id', $tenantId)->findOrFail($id);
|
||||
|
||||
// 작성중 상태에서만 삭제 가능
|
||||
if ($document->status !== Document::STATUS_DRAFT) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => '작성중 상태의 문서만 삭제할 수 있습니다.',
|
||||
], 422);
|
||||
}
|
||||
|
||||
$document->delete();
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => '문서가 삭제되었습니다.',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 문서 번호 생성
|
||||
*/
|
||||
private function generateDocumentNo(int $tenantId, int $templateId): string
|
||||
{
|
||||
$prefix = 'DOC';
|
||||
$date = now()->format('Ymd');
|
||||
|
||||
$lastDocument = Document::where('tenant_id', $tenantId)
|
||||
->where('template_id', $templateId)
|
||||
->whereDate('created_at', now()->toDateString())
|
||||
->orderBy('id', 'desc')
|
||||
->first();
|
||||
|
||||
$sequence = 1;
|
||||
if ($lastDocument) {
|
||||
// 마지막 문서 번호에서 시퀀스 추출
|
||||
$parts = explode('-', $lastDocument->document_no);
|
||||
if (count($parts) >= 3) {
|
||||
$sequence = (int) end($parts) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return sprintf('%s-%s-%04d', $prefix, $date, $sequence);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user