star-cloud/app/Http/Controllers/Api/V1/App/MachineAuthController.php
sky121113 86e895a4ae [FEAT] 實作智慧型機台登入異常告警與自動自我修復機制
1. [Models] 修改 Machine.php,增加 has_login_warning 動態屬性,設定未解決之登入失敗日誌須達 3 筆以上才亮起「登入異常」警報。
2. [Models] 重構 scopeHasAnyAlert 查詢,確保當未解決之登入失敗日誌未達 3 筆時,不會計入儀表板的「待處理告警 (Alerts Pending)」統計。
3. [API] 修改 MachineAuthController.php,在 B000 登入成功時加入自動自我修復 (Self-healing) 邏輯,一鍵將先前該機台的所有未解決登入失敗日誌標記為已解決 (is_resolved = true)。
4. [Views] 修改 admin.dashboard 及 admin.machines.index,新增登入異常警告 (Login Alert) 的琥珀色徽章與動態提示,並將儀表板上的機台卡片標題改為點擊可跳轉至對應機台之篩選列表。
5. [Lang] 更新 zh_TW.json、en.json 與 ja.json,新增多語系翻譯對照詞彙。
2026-05-18 09:50:51 +08:00

127 lines
4.0 KiB
PHP

<?php
namespace App\Http\Controllers\Api\V1\App;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use App\Models\System\User;
use App\Models\Machine\Machine;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log;
use App\Jobs\Machine\ProcessStateLog;
class MachineAuthController extends Controller
{
/**
* B000: 機台本機管理員/補貨員 離線同步登入驗證
* 這個 API 僅用於機台的 Java App 登入畫面驗證帳密。不必進入 Queue。
*/
public function loginB000(Request $request): JsonResponse
{
// 1. 驗證帳密與 IP
$validated = $request->validate([
'Su_Account' => 'required|string',
'Su_Password' => 'required|string',
'ip' => 'nullable|string',
]);
// 2. 取得機台物件 (由 iot.auth 中介層注入)
$machine = $request->get('machine');
// 3. 透過帳號尋找使用者 (允許使用 username 或 email)
$user = User::where('username', $validated['Su_Account'])
->orWhere('email', $validated['Su_Account'])
->first();
// 4. 驗證密碼
if (!$user || !Hash::check($validated['Su_Password'], $user->password)) {
Log::warning("B000 機台登入失敗: 帳密錯誤", [
'account' => $validated['Su_Account'],
'machine' => $machine->serial_no
]);
// 寫入機台日誌
ProcessStateLog::dispatch(
$machine->id,
$machine->company_id,
__("Login failed: :account", ['account' => $validated['Su_Account']]),
'warning',
[],
'login'
);
return response()->json([
'success' => false,
'code' => 401,
'message' => 'Failed'
], 401);
}
// 5. RBAC 權限驗證 (遵循多租戶與機台授權規範)
$isAuthorized = false;
if ($user->isSystemAdmin()) {
$isAuthorized = true;
} elseif ($user->is_admin) {
if ($machine->company_id === $user->company_id) {
$isAuthorized = true;
}
} else {
if ($user->machines()->where('machine_id', $machine->id)->exists()) {
$isAuthorized = true;
}
}
if (!$isAuthorized) {
Log::warning("B000 機台登入失敗: 權限不足", [
'user_id' => $user->id,
'machine_id' => $machine->id
]);
ProcessStateLog::dispatch(
$machine->id,
$machine->company_id,
__("Unauthorized login attempt: :account", ['account' => $user->username]),
'warning',
[],
'login'
);
return response()->json([
'success' => false,
'code' => 403,
'message' => 'Forbidden'
], 403);
}
// 6. 驗證完美通過!
Log::info("B000 機台登入成功", [
'account' => $user->username,
'machine' => $machine->serial_no
]);
// 自我修復:自動將先前所有未解決的登入失敗警告標記為已解決
$machine->logs()
->where('type', 'login')
->where('is_resolved', false)
->update(['is_resolved' => true]);
// 寫入成功登入日誌
ProcessStateLog::dispatch(
$machine->id,
$machine->company_id,
__("User logged in: :name", ['name' => $user->name ?? $user->username]),
'info',
[],
'login'
);
return response()->json([
'success' => true,
'code' => 200,
'message' => 'Success',
'token' => $user->createToken('technician-setup', ['*'], now()->addHours(8))->plainTextToken
]);
}
}