All checks were successful
star-cloud-deploy-demo / deploy-demo (push) Successful in 1m18s
1. 重構機台在線狀態判定機制:移除資料庫 status 欄位,改由 Model 根據心跳時間動態計算。 2. 修正儀表板 (Dashboard) 與機台管理頁面的多語系顯示問題,解決換行導致翻譯失效的 Bug。 3. 修正個人檔案頁面的麵包屑 (Breadcrumbs) 導航,補齊「個人設定」層級。 4. 更新 IoT API (B010, B600) 的認證機制與日誌處理邏輯。 5. 同步更新繁中、英文、日文語言檔,確保 UI 標籤一致性。
254 lines
9.9 KiB
PHP
254 lines
9.9 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Admin\BasicSettings;
|
|
|
|
use App\Http\Controllers\Admin\AdminController;
|
|
use App\Models\Machine\Machine;
|
|
use App\Models\Machine\MachineModel;
|
|
use App\Models\System\PaymentConfig;
|
|
use App\Traits\ImageHandler;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\View\View;
|
|
use Illuminate\Http\RedirectResponse;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Illuminate\Support\Str;
|
|
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class MachineSettingController extends AdminController
|
|
{
|
|
use ImageHandler;
|
|
|
|
/**
|
|
* 顯示機台與型號設定列表 (採用標籤頁整合)
|
|
*/
|
|
public function index(Request $request): View
|
|
{
|
|
$tab = $request->input('tab', 'machines');
|
|
$per_page = $request->input('per_page', 10);
|
|
$search = $request->input('search');
|
|
|
|
// 1. 處理機台清單 (Machines Tab)
|
|
$machineQuery = Machine::query()->with(['machineModel', 'paymentConfig', 'company']);
|
|
if ($tab === 'machines' && $search) {
|
|
$machineQuery->where(function ($q) use ($search) {
|
|
$q->where('name', 'like', "%{$search}%")
|
|
->orWhere('serial_no', 'like', "%{$search}%");
|
|
});
|
|
}
|
|
$machines = $machineQuery->latest()->paginate($per_page)->withQueryString();
|
|
|
|
// 2. 處理型號清單 (Models Tab)
|
|
$modelQuery = MachineModel::query()->withCount('machines');
|
|
if ($tab === 'models' && $search) {
|
|
$modelQuery->where('name', 'like', "%{$search}%");
|
|
}
|
|
$models_list = $modelQuery->latest()->paginate($per_page)->withQueryString();
|
|
|
|
// 3. 處理機台權限 (Permissions Tab) - 僅顯示 is_admin 帳號
|
|
$users_list = null;
|
|
if ($tab === 'permissions') {
|
|
$userQuery = \App\Models\System\User::query()
|
|
->where('is_admin', true)
|
|
->with(['company', 'machines']);
|
|
|
|
if ($search) {
|
|
$userQuery->where(function($q) use ($search) {
|
|
$q->where('name', 'like', "%{$search}%")
|
|
->orWhere('username', 'like', "%{$search}%");
|
|
});
|
|
}
|
|
|
|
if ($request->filled('company_id')) {
|
|
$userQuery->where('company_id', $request->company_id);
|
|
}
|
|
|
|
$users_list = $userQuery->latest()->paginate($per_page)->withQueryString();
|
|
}
|
|
|
|
// 4. 基礎下拉資料 (用於新增/編輯機台的彈窗)
|
|
$models = MachineModel::select('id', 'name')->get();
|
|
$paymentConfigs = PaymentConfig::select('id', 'name')->get();
|
|
$companies = \App\Models\System\Company::select('id', 'name', 'code')->get();
|
|
|
|
return view('admin.basic-settings.machines.index', compact(
|
|
'machines',
|
|
'models_list',
|
|
'users_list',
|
|
'models',
|
|
'paymentConfigs',
|
|
'companies',
|
|
'tab'
|
|
));
|
|
}
|
|
|
|
/**
|
|
* 儲存新機台 (僅核心欄位)
|
|
*/
|
|
public function store(Request $request): RedirectResponse
|
|
{
|
|
$validated = $request->validate([
|
|
'name' => 'required|string|max:255',
|
|
'serial_no' => 'required|string|unique:machines,serial_no',
|
|
'company_id' => 'nullable|exists:companies,id',
|
|
'machine_model_id' => 'required|exists:machine_models,id',
|
|
'payment_config_id' => 'nullable|exists:payment_configs,id',
|
|
'location' => 'nullable|string|max:255',
|
|
'images.*' => 'image|mimes:jpeg,png,jpg,gif,webp|max:10240', // Increase to 10MB
|
|
]);
|
|
|
|
$imagePaths = [];
|
|
if ($request->hasFile('images')) {
|
|
foreach (array_slice($request->file('images'), 0, 3) as $image) {
|
|
$imagePaths[] = $this->storeAsWebp($image, 'machines');
|
|
}
|
|
}
|
|
|
|
$machine = Machine::create(array_merge($validated, [
|
|
'api_token' => \Illuminate\Support\Str::random(60),
|
|
'creator_id' => auth()->id(),
|
|
'updater_id' => auth()->id(),
|
|
'card_reader_seconds' => 30, // 預設值
|
|
'card_reader_checkout_time_1' => '22:30:00',
|
|
'card_reader_checkout_time_2' => '23:45:00',
|
|
'payment_buffer_seconds' => 5,
|
|
'images' => $imagePaths,
|
|
]));
|
|
|
|
return redirect()->route('admin.basic-settings.machines.index')
|
|
->with('success', __('Machine created successfully.'));
|
|
}
|
|
|
|
/**
|
|
* 顯示詳細編輯頁面
|
|
*/
|
|
public function edit(Machine $machine): View
|
|
{
|
|
$models = MachineModel::select('id', 'name')->get();
|
|
$paymentConfigs = PaymentConfig::select('id', 'name')->get();
|
|
$companies = \App\Models\System\Company::select('id', 'name', 'code')->get();
|
|
|
|
return view('admin.basic-settings.machines.edit', compact('machine', 'models', 'paymentConfigs', 'companies'));
|
|
}
|
|
|
|
/**
|
|
* 更新機台詳細參數
|
|
*/
|
|
public function update(Request $request, Machine $machine): RedirectResponse
|
|
{
|
|
Log::info('Machine Update Request', ['machine_id' => $machine->id, 'data' => $request->all()]);
|
|
|
|
try {
|
|
$validated = $request->validate([
|
|
'name' => 'required|string|max:255',
|
|
'serial_no' => 'sometimes|required|string|unique:machines,serial_no,' . $machine->id,
|
|
'card_reader_seconds' => 'required|integer|min:0',
|
|
'payment_buffer_seconds' => 'required|integer|min:0',
|
|
'card_reader_checkout_time_1' => 'nullable|string',
|
|
'card_reader_checkout_time_2' => 'nullable|string',
|
|
'heating_start_time' => 'nullable|string',
|
|
'heating_end_time' => 'nullable|string',
|
|
'card_reader_no' => 'nullable|string|max:255',
|
|
'key_no' => 'nullable|string|max:255',
|
|
'invoice_status' => 'required|integer|in:0,1,2',
|
|
'welcome_gift_enabled' => 'boolean',
|
|
'is_spring_slot_1_10' => 'boolean',
|
|
'is_spring_slot_11_20' => 'boolean',
|
|
'is_spring_slot_21_30' => 'boolean',
|
|
'is_spring_slot_31_40' => 'boolean',
|
|
'is_spring_slot_41_50' => 'boolean',
|
|
'is_spring_slot_51_60' => 'boolean',
|
|
'member_system_enabled' => 'boolean',
|
|
'machine_model_id' => 'required|exists:machine_models,id',
|
|
'payment_config_id' => 'nullable|exists:payment_configs,id',
|
|
'location' => 'nullable|string|max:255',
|
|
'image_0' => 'nullable|image|mimes:jpeg,png,jpg,gif,webp|max:10240',
|
|
'image_1' => 'nullable|image|mimes:jpeg,png,jpg,gif,webp|max:10240',
|
|
'image_2' => 'nullable|image|mimes:jpeg,png,jpg,gif,webp|max:10240',
|
|
'remove_image_0' => 'nullable|boolean',
|
|
'remove_image_1' => 'nullable|boolean',
|
|
'remove_image_2' => 'nullable|boolean',
|
|
]);
|
|
|
|
// 僅限系統管理員可修改公司
|
|
if (auth()->user()->isSystemAdmin()) {
|
|
$companyRule = ['company_id' => 'nullable|exists:companies,id'];
|
|
$companyData = $request->validate($companyRule);
|
|
$validated = array_merge($validated, $companyData);
|
|
}
|
|
|
|
Log::info('Machine Update Validated Data', ['data' => $validated]);
|
|
} catch (\Illuminate\Validation\ValidationException $e) {
|
|
Log::error('Machine Update Validation Failed', ['errors' => $e->errors()]);
|
|
throw $e;
|
|
}
|
|
|
|
// 排除虛擬欄位 (圖片上傳、移除標記),這些欄位不在資料表內
|
|
$dataToUpdate = \Illuminate\Support\Arr::except($validated, [
|
|
'image_0', 'image_1', 'image_2',
|
|
'remove_image_0', 'remove_image_1', 'remove_image_2'
|
|
]);
|
|
|
|
$machine->update(array_merge($dataToUpdate, [
|
|
'updater_id' => auth()->id(),
|
|
]));
|
|
|
|
// 處理圖片更新 (支援 3 個獨立槽位: image_0, image_1, image_2)
|
|
$currentImages = $machine->images ?? [];
|
|
$updated = false;
|
|
|
|
for ($i = 0; $i < 3; $i++) {
|
|
$inputName = "image_$i";
|
|
$removeName = "remove_image_$i";
|
|
|
|
// 如果有新圖片上傳
|
|
if ($request->hasFile($inputName)) {
|
|
// 刪除舊圖
|
|
if (isset($currentImages[$i]) && !empty($currentImages[$i])) {
|
|
\Illuminate\Support\Facades\Storage::disk('public')->delete($currentImages[$i]);
|
|
}
|
|
// 儲存新圖
|
|
$currentImages[$i] = $this->storeAsWebp($request->file($inputName), 'machines');
|
|
$updated = true;
|
|
}
|
|
// 否則,如果有刪除標記
|
|
elseif ($request->input($removeName) === '1') {
|
|
if (isset($currentImages[$i]) && !empty($currentImages[$i])) {
|
|
\Illuminate\Support\Facades\Storage::disk('public')->delete($currentImages[$i]);
|
|
unset($currentImages[$i]);
|
|
$updated = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($updated) {
|
|
ksort($currentImages);
|
|
$machine->update(['images' => array_values($currentImages)]);
|
|
}
|
|
|
|
return redirect()->route('admin.basic-settings.machines.index')
|
|
->with('success', __('Machine settings updated successfully.'));
|
|
}
|
|
|
|
public function regenerateToken(Request $request, $serial): \Illuminate\Http\JsonResponse
|
|
{
|
|
$machine = Machine::where('serial_no', $serial)->firstOrFail();
|
|
$newToken = \Illuminate\Support\Str::random(60);
|
|
$machine->update(['api_token' => $newToken]);
|
|
|
|
Log::info('Machine API Token Regenerated', [
|
|
'machine_id' => $machine->id,
|
|
'serial_no' => $machine->serial_no,
|
|
'user_id' => auth()->id()
|
|
]);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => __('API Token regenerated successfully.'),
|
|
'api_token' => $newToken
|
|
]);
|
|
}
|
|
|
|
|
|
}
|