From 0685c71141185bb2759f8bba801b0b737c49720c Mon Sep 17 00:00:00 2001 From: sky121113 Date: Fri, 15 May 2026 13:50:33 +0800 Subject: [PATCH 1/3] =?UTF-8?q?[FIX]=20=E4=BF=AE=E5=BE=A9=E5=BB=A3?= =?UTF-8?q?=E5=91=8A=E5=9C=96=E7=89=87=E4=B8=8A=E5=82=B3=E5=A4=B1=E6=95=97?= =?UTF-8?q?=E8=88=87=E5=84=AA=E5=8C=96=E9=8C=AF=E8=AA=A4=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 修正正式環境 Nginx client_max_body_size 限制導致的上傳失敗問題(已於 Host 端同步修正)。 2. 在 AdvertisementController@store 加入 try-catch 區塊與 Log 紀錄,確保圖片處理異常時回傳 JSON 錯誤。 3. 優化前端上傳 AJAX 邏輯,當發生 413 (檔案太大) 或 500 錯誤時提供明確的提示訊息。 --- .../Admin/AdvertisementController.php | 81 +++++++++++-------- resources/views/admin/ads/index.blade.php | 53 +++++++----- 2 files changed, 80 insertions(+), 54 deletions(-) diff --git a/app/Http/Controllers/Admin/AdvertisementController.php b/app/Http/Controllers/Admin/AdvertisementController.php index 2c30434..87b7a94 100644 --- a/app/Http/Controllers/Admin/AdvertisementController.php +++ b/app/Http/Controllers/Admin/AdvertisementController.php @@ -76,42 +76,59 @@ class AdvertisementController extends AdminController 'end_at' => 'nullable|date|after_or_equal:start_at', ]); - $user = auth()->user(); - $file = $request->file('file'); - - if ($request->type === 'image') { - $path = $this->storeAsWebp($file, 'ads'); - } else { - $path = $file->store('ads', 'public'); - } + try { + $user = auth()->user(); + $file = $request->file('file'); + + if ($request->type === 'image') { + $path = $this->storeAsWebp($file, 'ads'); + } else { + $path = $file->store('ads', 'public'); + } - if ($user->isSystemAdmin()) { - $companyId = $request->filled('company_id') ? $request->company_id : null; - } else { - $companyId = $user->company_id; - } + if ($user->isSystemAdmin()) { + $companyId = $request->filled('company_id') ? $request->company_id : null; + } else { + $companyId = $user->company_id; + } - $advertisement = Advertisement::create([ - 'company_id' => $companyId, - 'name' => $request->name, - 'type' => $request->type, - 'duration' => (int) $request->duration, - 'url' => Storage::disk('public')->url($path), - 'is_active' => true, - 'start_at' => $request->start_at, - 'end_at' => $request->end_at, - ]); - - if ($request->wantsJson()) { - session()->flash('success', __('Advertisement created successfully.')); - return response()->json([ - 'success' => true, - 'message' => __('Advertisement created successfully.'), - 'data' => $advertisement + $advertisement = Advertisement::create([ + 'company_id' => $companyId, + 'name' => $request->name, + 'type' => $request->type, + 'duration' => (int) $request->duration, + 'url' => Storage::disk('public')->url($path), + 'is_active' => true, + 'start_at' => $request->start_at, + 'end_at' => $request->end_at, ]); - } - return redirect()->back()->with('success', __('Advertisement created successfully.')); + if ($request->wantsJson()) { + return response()->json([ + 'success' => true, + 'message' => __('Advertisement created successfully.'), + 'data' => $advertisement + ]); + } + + return redirect()->back()->with('success', __('Advertisement created successfully.')); + + } catch (\Exception $e) { + \Log::error('Advertisement Upload Error: ' . $e->getMessage(), [ + 'user_id' => auth()->id(), + 'file_name' => $request->file('file')?->getClientOriginalName(), + 'trace' => $e->getTraceAsString() + ]); + + if ($request->wantsJson()) { + return response()->json([ + 'success' => false, + 'message' => __('Failed to process advertisement file: ') . $e->getMessage() + ], 500); + } + + return redirect()->back()->with('error', __('Failed to process advertisement file.')); + } } public function update(Request $request, Advertisement $advertisement) diff --git a/resources/views/admin/ads/index.blade.php b/resources/views/admin/ads/index.blade.php index cd0c993..ebfdbaa 100644 --- a/resources/views/admin/ads/index.blade.php +++ b/resources/views/admin/ads/index.blade.php @@ -1139,30 +1139,39 @@ $baseRoute = 'admin.data-config.advertisements'; }); xhr.addEventListener('load', () => { + let result; try { - const result = JSON.parse(xhr.responseText); - if (xhr.status >= 200 && xhr.status < 300 && result.success) { - // 進度跳到 100% - if (bar) bar.style.width = '100%'; - this.uploadProgress = 100; - this.isAdModalOpen = false; - // 延遲 300ms 讓進度條動畫完成後再重整 - setTimeout(() => { - location.reload(); - }, 300); - } else { - window.showToast?.(result.message || 'Error', 'error'); - this.isSubmitting = false; - this.uploadProgress = 0; - if (bar) { - bar.style.width = '0%'; - bar.style.opacity = '0'; - bar.classList.remove('loading'); - } - } + result = JSON.parse(xhr.responseText); } catch (e) { - console.error('Failed to parse response', e); - window.showToast?.('System Error', 'error'); + console.error('Failed to parse response', e, xhr.responseText); + let errorMsg = 'System Error'; + if (xhr.status === 413) { + errorMsg = '{{ __("File is too large for the server configuration") }} (Nginx 413)'; + } else if (xhr.status >= 500) { + errorMsg = '{{ __("Server Internal Error") }} (500)'; + } + window.showToast?.(errorMsg, 'error'); + this.isSubmitting = false; + this.uploadProgress = 0; + if (bar) { + bar.style.width = '0%'; + bar.style.opacity = '0'; + bar.classList.remove('loading'); + } + return; + } + + if (xhr.status >= 200 && xhr.status < 300 && result.success) { + // 進度跳到 100% + if (bar) bar.style.width = '100%'; + this.uploadProgress = 100; + this.isAdModalOpen = false; + // 延遲 300ms 讓進度條動畫完成後再重整 + setTimeout(() => { + location.reload(); + }, 300); + } else { + window.showToast?.(result.message || 'Error', 'error'); this.isSubmitting = false; this.uploadProgress = 0; if (bar) { From 313ab72056352d53fc0b552d5b522e98dd84f1e9 Mon Sep 17 00:00:00 2001 From: sky121113 Date: Fri, 15 May 2026 13:53:24 +0800 Subject: [PATCH 2/3] =?UTF-8?q?[FIX]=20=E8=A3=9C=E5=9B=9E=E4=B8=8A?= =?UTF-8?q?=E5=82=B3=E6=88=90=E5=8A=9F=E5=BE=8C=E7=9A=84=20session=20flash?= =?UTF-8?q?=20=E8=A8=8A=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Controllers/Admin/AdvertisementController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Http/Controllers/Admin/AdvertisementController.php b/app/Http/Controllers/Admin/AdvertisementController.php index 87b7a94..459799f 100644 --- a/app/Http/Controllers/Admin/AdvertisementController.php +++ b/app/Http/Controllers/Admin/AdvertisementController.php @@ -104,6 +104,7 @@ class AdvertisementController extends AdminController ]); if ($request->wantsJson()) { + session()->flash('success', __('Advertisement created successfully.')); return response()->json([ 'success' => true, 'message' => __('Advertisement created successfully.'), From 143050a5c618b141418de885cdba267657525664 Mon Sep 17 00:00:00 2001 From: sky121113 Date: Fri, 15 May 2026 14:01:50 +0800 Subject: [PATCH 3/3] fix: improve advertisement notification feedback and error handling --- .../Admin/AdvertisementController.php | 3 +++ resources/views/admin/ads/index.blade.php | 26 ++++++++++++------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/app/Http/Controllers/Admin/AdvertisementController.php b/app/Http/Controllers/Admin/AdvertisementController.php index 87b7a94..2d51b71 100644 --- a/app/Http/Controllers/Admin/AdvertisementController.php +++ b/app/Http/Controllers/Admin/AdvertisementController.php @@ -104,6 +104,7 @@ class AdvertisementController extends AdminController ]); if ($request->wantsJson()) { + session()->flash('success', __('Advertisement created successfully.')); return response()->json([ 'success' => true, 'message' => __('Advertisement created successfully.'), @@ -206,6 +207,7 @@ class AdvertisementController extends AdminController $advertisement->delete(); if ($request->wantsJson()) { + session()->flash('success', __('Advertisement deleted successfully.')); return response()->json([ 'success' => true, 'message' => __('Advertisement deleted successfully.') @@ -387,6 +389,7 @@ class AdvertisementController extends AdminController $status = $advertisement->is_active ? __('Enabled') : __('Disabled'); if (request()->ajax()) { + session()->flash('success', __('Advertisement status updated to :status', ['status' => $status])); return response()->json([ 'success' => true, 'message' => __('Advertisement status updated to :status', ['status' => $status]), diff --git a/resources/views/admin/ads/index.blade.php b/resources/views/admin/ads/index.blade.php index ebfdbaa..49b1c47 100644 --- a/resources/views/admin/ads/index.blade.php +++ b/resources/views/admin/ads/index.blade.php @@ -1035,6 +1035,7 @@ $baseRoute = 'admin.data-config.advertisements'; method: 'POST', headers: { 'Content-Type': 'application/json', + 'Accept': 'application/json', 'X-CSRF-TOKEN': '{{ csrf_token() }}' }, body: JSON.stringify({ assignment_ids: assignmentIds }) @@ -1171,7 +1172,12 @@ $baseRoute = 'admin.data-config.advertisements'; location.reload(); }, 300); } else { - window.showToast?.(result.message || 'Error', 'error'); + let errorMessage = result.message || 'Error'; + if (result.errors) { + // 如果有驗證錯誤,彙整所有錯誤訊息 + errorMessage = Object.values(result.errors).flat().join(' '); + } + window.showToast?.(errorMessage, 'error'); this.isSubmitting = false; this.uploadProgress = 0; if (bar) { @@ -1223,22 +1229,21 @@ $baseRoute = 'admin.data-config.advertisements'; body: JSON.stringify(this.assignForm) }); - if (!response.ok) { - const errorText = await response.text(); - console.error('Server error response:', errorText); - throw new Error(`Server responded with ${response.status}`); - } - const result = await response.json(); - if (result.success) { + if (response.ok && result.success) { this.isAssignModalOpen = false; this.fetchMachineAds(); window.showToast?.(result.message, 'success'); } else { - window.showToast?.(result.message || 'Error', 'error'); + let msg = result.message || 'Error'; + if (result.errors) { + msg = Object.values(result.errors).flat().join(' '); + } + window.showToast?.(msg, 'error'); } } catch (e) { console.error('Failed to assign ad', e); + window.showToast?.('{{ __("System Error") }}', 'error'); } }, @@ -1409,6 +1414,7 @@ $baseRoute = 'admin.data-config.advertisements'; const response = await fetch(url, { method: 'DELETE', headers: { + 'Accept': 'application/json', 'X-CSRF-TOKEN': '{{ csrf_token() }}' } }); @@ -1416,6 +1422,8 @@ $baseRoute = 'admin.data-config.advertisements'; if (result.success) { this.fetchMachineAds(); window.showToast?.(result.message, 'success'); + } else { + window.showToast?.(result.message || 'Error', 'error'); } } catch (e) { console.error('Failed to remove assignment', e);