Files
star-cloud/app/Http/Controllers/Admin/AdvertisementController.php
sky121113 54d62c5378
All checks were successful
star-cloud-deploy-demo / deploy-demo (push) Successful in 1m7s
[FEAT] 實作機台廣告管理模組與多語系支援
1. 新增廣告管理列表與機台配置介面,包含多語系 (zh_TW, en, ja) 與完整 CRUD
2. 實作基於 Alpine 的廣告素材預覽輪播功能
3. 優化廣告素材下拉選單,強制綁定所屬公司以達成多租戶資料隔離
4. 重構廣告配置中廣告影片的縮圖渲染邏輯,移除 <video> 標籤以大幅提升頁面載入速度與節省頻寬
5. 放寬個人檔案頭像上傳限制,支援 WebP 格式
2026-03-31 13:30:41 +08:00

222 lines
7.0 KiB
PHP

<?php
namespace App\Http\Controllers\Admin;
use App\Models\Machine\Machine;
use App\Models\Machine\MachineAdvertisement;
use App\Models\System\Advertisement;
use App\Models\System\Company;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
class AdvertisementController extends AdminController
{
public function index(Request $request)
{
$user = auth()->user();
$tab = $request->input('tab', 'list');
// Tab 1: 廣告列表
$advertisements = Advertisement::with('company')->latest()->paginate(10);
$allAds = Advertisement::active()->get();
// Tab 2: 機台廣告設置 (所需資料)
// 取得使用者有權限的機台列表 (已透過 Global Scope 過濾)
$machines = Machine::select('id', 'name', 'serial_no', 'company_id')->get();
$companies = $user->isSystemAdmin() ? Company::orderBy('name')->get() : collect();
return view('admin.ads.index', [
'advertisements' => $advertisements,
'machines' => $machines,
'tab' => $tab,
'allAds' => $allAds,
'companies' => $companies,
]);
}
/**
* 素材 CRUD: 儲存廣告
*/
public function store(Request $request)
{
$request->validate([
'name' => 'required|string|max:255',
'type' => 'required|in:image,video',
'duration' => 'required|in:15,30,60',
'file' => [
'required',
'file',
'mimes:jpeg,png,jpg,gif,webp,mp4,mov,avi',
$request->type === 'image' ? 'max:5120' : 'max:51200', // Image 5MB, Video 50MB
],
'company_id' => 'nullable|exists:companies,id',
]);
$user = auth()->user();
$path = $request->file('file')->store('ads', 'public');
if ($user->isSystemAdmin()) {
$companyId = $request->filled('company_id') ? $request->company_id : null;
} else {
$companyId = $user->company_id;
}
Advertisement::create([
'company_id' => $companyId,
'name' => $request->name,
'type' => $request->type,
'duration' => (int) $request->duration,
'url' => Storage::disk('public')->url($path),
'is_active' => true,
]);
return redirect()->back()->with('success', __('Advertisement created successfully.'));
}
public function update(Request $request, Advertisement $advertisement)
{
$rules = [
'name' => 'required|string|max:255',
'type' => 'required|in:image,video',
'duration' => 'required|in:15,30,60',
'is_active' => 'boolean',
'company_id' => 'nullable|exists:companies,id',
];
if ($request->hasFile('file')) {
$rules['file'] = [
'file',
'mimes:jpeg,png,jpg,gif,webp,mp4,mov,avi',
$request->type === 'image' ? 'max:5120' : 'max:51200',
];
}
$request->validate($rules);
$data = $request->only(['name', 'type', 'duration']);
$data['is_active'] = $request->has('is_active');
$user = auth()->user();
if ($user->isSystemAdmin()) {
$data['company_id'] = $request->filled('company_id') ? $request->company_id : null;
}
if ($request->hasFile('file')) {
// 刪除舊檔案
$oldPath = str_replace(Storage::disk('public')->url(''), '', $advertisement->url);
Storage::disk('public')->delete($oldPath);
// 存入新檔案
$path = $request->file('file')->store('ads', 'public');
$data['url'] = Storage::disk('public')->url($path);
}
$advertisement->update($data);
return redirect()->back()->with('success', __('Advertisement updated successfully.'));
}
public function destroy(Advertisement $advertisement)
{
// 檢查是否有機台正投放中
if ($advertisement->machineAdvertisements()->exists()) {
return redirect()->back()->with('error', __('Cannot delete advertisement being used by machines.'));
}
// 刪除實體檔案
$path = str_replace(Storage::disk('public')->url(''), '', $advertisement->url);
Storage::disk('public')->delete($path);
$advertisement->delete();
return redirect()->back()->with('success', __('Advertisement deleted successfully.'));
}
/**
* AJAX: 取得特定機台的廣告投放清單
*/
public function getMachineAds(Machine $machine)
{
$assignments = MachineAdvertisement::where('machine_id', $machine->id)
->with('advertisement')
->get()
->groupBy('position');
return response()->json([
'success' => true,
'data' => $assignments
]);
}
/**
* 投放廣告至機台
*/
public function assign(Request $request)
{
$request->validate([
'machine_id' => 'required|exists:machines,id',
'advertisement_id' => 'required|exists:advertisements,id',
'position' => 'required|in:vending,visit_gift,standby',
'sort_order' => 'nullable|integer',
]);
// If sort_order is not provided, append to the end of the current position list
$newSortOrder = $request->sort_order;
if (is_null($newSortOrder)) {
$newSortOrder = MachineAdvertisement::where('machine_id', $request->machine_id)
->where('position', $request->position)
->max('sort_order') + 1;
}
MachineAdvertisement::updateOrCreate(
[
'machine_id' => $request->machine_id,
'position' => $request->position,
'advertisement_id' => $request->advertisement_id,
],
[
'sort_order' => $newSortOrder,
]
);
return response()->json([
'success' => true,
'message' => __('Advertisement assigned successfully.')
]);
}
/**
* 重新排序廣告播放順序
*/
public function reorderAssignments(Request $request)
{
$request->validate([
'assignment_ids' => 'required|array',
'assignment_ids.*' => 'exists:machine_advertisements,id'
]);
foreach ($request->assignment_ids as $index => $id) {
MachineAdvertisement::where('id', $id)->update(['sort_order' => $index]);
}
return response()->json([
'success' => true,
'message' => __('Order updated successfully.')
]);
}
/**
* 移除廣告投放
*/
public function removeAssignment($id)
{
$assignment = MachineAdvertisement::findOrFail($id);
$assignment->delete();
return response()->json([
'success' => true,
'message' => __('Assignment removed successfully.')
]);
}
}