query('search', ''); $withTrashed = $request->boolean('with_trashed', false); $query = PmisNotice::tenant($this->tenantId()) ->with('author:id,name') ->withCount('attachments') ->orderByDesc('id'); if ($withTrashed && auth()->user()->isAdmin()) { $query->withTrashed(); } if ($search) { $query->where('title', 'like', "%{$search}%"); } $notices = $query->get()->map(fn ($n) => [ 'id' => $n->id, 'title' => $n->title, 'author' => $n->author?->name ?? '관리자', 'createdAt' => $n->created_at->format('Y-m-d H:i:s'), 'views' => $n->views, 'hasAttachment' => $n->attachments_count > 0, 'isDeleted' => $n->trashed(), ]); return response()->json(['notices' => $notices]); } public function show(int $id): JsonResponse { $notice = PmisNotice::tenant($this->tenantId()) ->with(['author:id,name', 'attachments']) ->findOrFail($id); $notice->increment('views'); return response()->json([ 'notice' => [ 'id' => $notice->id, 'title' => $notice->title, 'content' => $notice->content, 'author' => $notice->author?->name ?? '관리자', 'createdAt' => $notice->created_at->format('Y-m-d H:i:s'), 'views' => $notice->views, 'attachments' => $notice->attachments->map(fn ($a) => [ 'id' => $a->id, 'fileName' => $a->original_name, 'size' => PmisNoticeAttachment::formatSize($a->file_size), 'downloadUrl' => "/juil/construction-pmis/api/notices/attachments/{$a->id}/download", ]), ], ]); } public function store(Request $request): JsonResponse { $user = auth()->user(); if (! $user->isAdmin()) { return response()->json(['message' => '권한이 없습니다.'], 403); } $request->validate([ 'title' => 'required|string|max:300', 'content' => 'nullable|string', 'files' => 'nullable|array', 'files.*' => 'file|max:51200', ]); $notice = PmisNotice::create([ 'tenant_id' => $this->tenantId(), 'title' => $request->title, 'content' => $request->content ?? '', 'author_id' => $user->id, ]); if ($request->hasFile('files')) { foreach ($request->file('files') as $file) { $path = $file->store('pmis/notices', 'public'); $notice->attachments()->create([ 'original_name' => $file->getClientOriginalName(), 'file_path' => $path, 'file_size' => $file->getSize(), ]); } } return response()->json(['notice' => $notice->load('attachments'), 'message' => '등록되었습니다.'], 201); } public function update(Request $request, int $id): JsonResponse { $user = auth()->user(); if (! $user->isAdmin()) { return response()->json(['message' => '권한이 없습니다.'], 403); } $notice = PmisNotice::tenant($this->tenantId())->findOrFail($id); $request->validate([ 'title' => 'required|string|max:300', 'content' => 'nullable|string', 'files' => 'nullable|array', 'files.*' => 'file|max:51200', ]); $notice->update([ 'title' => $request->title, 'content' => $request->content ?? '', ]); if ($request->hasFile('files')) { foreach ($request->file('files') as $file) { $path = $file->store('pmis/notices', 'public'); $notice->attachments()->create([ 'original_name' => $file->getClientOriginalName(), 'file_path' => $path, 'file_size' => $file->getSize(), ]); } } return response()->json(['notice' => $notice->load('attachments'), 'message' => '수정되었습니다.']); } public function destroy(int $id): JsonResponse { $user = auth()->user(); if (! $user->isAdmin()) { return response()->json(['message' => '권한이 없습니다.'], 403); } $notice = PmisNotice::tenant($this->tenantId())->findOrFail($id); foreach ($notice->attachments as $att) { Storage::disk('public')->delete($att->file_path); } $notice->delete(); return response()->json(['message' => '삭제되었습니다.']); } public function attachmentDownload(int $id) { $att = PmisNoticeAttachment::findOrFail($id); $fullPath = Storage::disk('public')->path($att->file_path); return response()->download($fullPath, $att->original_name); } public function attachmentDestroy(int $id): JsonResponse { $user = auth()->user(); if (! $user->isAdmin()) { return response()->json(['message' => '권한이 없습니다.'], 403); } $att = PmisNoticeAttachment::findOrFail($id); Storage::disk('public')->delete($att->file_path); $att->delete(); return response()->json(['message' => '삭제되었습니다.']); } }