All checks were successful
star-cloud-deploy-demo / deploy-demo (push) Successful in 1m11s
1. 實作遠端指令去重機制 (Supersede):避免重複下達相同待執行指令。 2. 修正遠端指令發送後的 Toast 提示邏輯,確保頁面跳轉後正確顯示回饋。 3. 增加 RemoteCommand 操作者 (user_id) 紀錄與狀態列舉擴充 (superseded)。 4. 修復機台列表「最後頁面」欄位對照錯誤,同步更新 Machine Model 與 API 規格。 5. 優化遠端指令中心 UI:放大卡片字體、調整側面欄間距,符合極簡奢華風規範。 6. 更新 API 技術規格書 (SKILL.md) 與 config/api-docs.php,補全所有機台代碼 (66-611) 與指令。 7. 補全繁體中文、英文、日文多語系翻譯檔案。
170 lines
4.6 KiB
PHP
170 lines
4.6 KiB
PHP
<?php
|
||
|
||
namespace App\Models\Machine;
|
||
|
||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||
use Illuminate\Database\Eloquent\Model;
|
||
|
||
use App\Traits\TenantScoped;
|
||
|
||
class Machine extends Model
|
||
{
|
||
use HasFactory, TenantScoped;
|
||
use \Illuminate\Database\Eloquent\SoftDeletes;
|
||
|
||
protected static function booted()
|
||
{
|
||
// 權限隔離:一般帳號登入時只能看到自己被分配的機台
|
||
static::addGlobalScope('machine_access', function (\Illuminate\Database\Eloquent\Builder $builder) {
|
||
$user = auth()->user();
|
||
// 如果是在 Console、或是系統管理員,則不限制 (可看所有機台)
|
||
if (app()->runningInConsole() || !$user || $user->isSystemAdmin()) {
|
||
return;
|
||
}
|
||
|
||
// 一般租戶帳號:限制只能看自己擁有的機台
|
||
$builder->whereExists(function ($query) use ($user) {
|
||
$query->select(\Illuminate\Support\Facades\DB::raw(1))
|
||
->from('machine_user')
|
||
->whereColumn('machine_user.machine_id', 'machines.id')
|
||
->where('machine_user.user_id', $user->id);
|
||
});
|
||
});
|
||
}
|
||
|
||
protected $fillable = [
|
||
'company_id',
|
||
'name',
|
||
'serial_no',
|
||
'model',
|
||
'location',
|
||
'status',
|
||
'current_page',
|
||
'door_status',
|
||
'temperature',
|
||
'firmware_version',
|
||
'api_token',
|
||
'last_heartbeat_at',
|
||
'card_reader_seconds',
|
||
'card_reader_checkout_time_1',
|
||
'card_reader_checkout_time_2',
|
||
'heating_start_time',
|
||
'heating_end_time',
|
||
'payment_buffer_seconds',
|
||
'card_reader_no',
|
||
'key_no',
|
||
'invoice_status',
|
||
'welcome_gift_enabled',
|
||
'is_spring_slot_1_10',
|
||
'is_spring_slot_11_20',
|
||
'is_spring_slot_21_30',
|
||
'is_spring_slot_31_40',
|
||
'is_spring_slot_41_50',
|
||
'is_spring_slot_51_60',
|
||
'member_system_enabled',
|
||
'payment_config_id',
|
||
'machine_model_id',
|
||
'images',
|
||
'creator_id',
|
||
'updater_id',
|
||
];
|
||
|
||
protected $appends = ['image_urls'];
|
||
|
||
protected $casts = [
|
||
'last_heartbeat_at' => 'datetime',
|
||
'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',
|
||
'images' => 'array',
|
||
];
|
||
|
||
/**
|
||
* Get machine images absolute URLs
|
||
*/
|
||
public function getImageUrlsAttribute(): array
|
||
{
|
||
if (empty($this->images)) {
|
||
return [];
|
||
}
|
||
|
||
return array_map(fn($path) => \Illuminate\Support\Facades\Storage::disk('public')->url($path), $this->images);
|
||
}
|
||
|
||
public function logs()
|
||
{
|
||
return $this->hasMany(MachineLog::class);
|
||
}
|
||
|
||
public function slots()
|
||
{
|
||
return $this->hasMany(MachineSlot::class);
|
||
}
|
||
|
||
public function commands()
|
||
{
|
||
return $this->hasMany(RemoteCommand::class);
|
||
}
|
||
|
||
public function machineModel()
|
||
{
|
||
return $this->belongsTo(MachineModel::class);
|
||
}
|
||
|
||
public function paymentConfig()
|
||
{
|
||
return $this->belongsTo(\App\Models\System\PaymentConfig::class);
|
||
}
|
||
|
||
public function creator()
|
||
{
|
||
return $this->belongsTo(\App\Models\System\User::class, 'creator_id');
|
||
}
|
||
|
||
public function updater()
|
||
{
|
||
return $this->belongsTo(\App\Models\System\User::class, 'updater_id');
|
||
}
|
||
|
||
public const PAGE_STATUSES = [
|
||
'0' => 'Offline',
|
||
'1' => 'Home Page',
|
||
'2' => 'Vending Page',
|
||
'3' => 'Admin Page',
|
||
'4' => 'Replenishment Page',
|
||
'5' => 'Tutorial Page',
|
||
'6' => 'Purchasing',
|
||
'7' => 'Locked Page',
|
||
'60' => 'Dispense Success',
|
||
'61' => 'Slot Test',
|
||
'62' => 'Payment Selection',
|
||
'63' => 'Waiting for Payment',
|
||
'64' => 'Dispensing',
|
||
'65' => 'Receipt Printing',
|
||
'66' => 'Pass Code',
|
||
'67' => 'Pickup Code',
|
||
'68' => 'Message Display',
|
||
'69' => 'Cancel Purchase',
|
||
'610' => 'Purchase Finished',
|
||
'611' => 'Welcome Gift',
|
||
'612' => 'Dispense Failed',
|
||
];
|
||
|
||
public function getCurrentPageLabelAttribute(): string
|
||
{
|
||
$code = (string) $this->current_page;
|
||
$label = self::PAGE_STATUSES[$code] ?? $code;
|
||
return __($label);
|
||
}
|
||
|
||
public function users()
|
||
{
|
||
return $this->belongsToMany(\App\Models\System\User::class);
|
||
}
|
||
}
|