Files
CFDivePlatform/app/Models/DivingOffer.php
T
a620906209 43720482c4
Run Tests / test (pull_request) Successful in 2m3s
feat(verification): 教練審核流程完整版——狀態機/證照送審/Admin 裁決/通知
實作 openspec change provider-verification-workflow(25/25 tasks):
- is_verified 升級為 verification_status 四狀態機(unsubmitted/pending/
  approved/rejected),migration 自動轉換既有資料,API 以 accessor 保留
  is_verified 相容輸出
- 教練端:證照上傳(≤3 張、複用 CompressesImages)、送審、駁回原因
  顯示與重送(/coach/verification 新頁面)
- Admin 端:審核佇列、通過/駁回(原因必填)、撤銷 approved;移除
  toggle-verified 端點(防繞過狀態機);裁決後 flush 課程快取
- 通知:審核通過/駁回(站內+Email,駁回含原因)
- 可見性與預約入口改判 approved(行為等價)
- 測試 +18(ProviderVerificationTest 10、AdminVerificationTest 8),
  既有 4 個測試檔 helper 遷移;Swagger 同步
- 規格同步:provider-verification 全面改寫、admin-user-management
  toggle 段落改為移除聲明

容器內全套件 173 passed / 439 assertions。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 00:26:12 +08:00

84 lines
2.1 KiB
PHP

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Facades\Storage;
class DivingOffer extends Model
{
public $timestamps = false;
protected $table = 'diving_offers';
protected $fillable = [
'provider_id',
'title',
'location',
'spot',
'rating',
'reviews',
'price',
'badges',
'description',
'tag',
'region',
'cover_image',
];
protected $casts = [
'badges' => 'array',
'rating' => 'float',
'price' => 'integer',
'reviews'=> 'integer',
];
protected static function booted(): void
{
static::deleting(function ($offer) {
Storage::disk('public')->deleteDirectory("offers/{$offer->id}");
});
}
public function getCoverImageUrlAttribute(): ?string
{
return $this->cover_image
? Storage::disk('public')->url($this->cover_image)
: null;
}
public function provider(): BelongsTo
{
return $this->belongsTo(User::class, 'provider_id');
}
/**
* 公開端點可見性:未通過審核(approved)教練的課程不對外曝光。
* provider_id 為 null 的課程(平台自有資料)不受此限制。
*/
public function scopeVisibleToPublic(Builder $query): Builder
{
return $query->where(function (Builder $visible) {
$visible->whereNull('provider_id')
->orWhereHas('provider.providerProfile', fn (Builder $profile) => $profile->where('verification_status', \App\Enums\VerificationStatus::Approved->value));
});
}
public function schedules()
{
return $this->hasMany(CourseSchedule::class, 'diving_offer_id');
}
public function courseImages()
{
return $this->hasMany(CourseImage::class)->orderBy('sort_order');
}
public function reviews()
{
return $this->hasMany(Review::class);
}
}