You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

211 lines
6.0 KiB

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Venue extends Model
{
use HasFactory;
protected $fillable = [
'name',
'venue_type',
'venue_types',
'unit_name',
'district',
'ticket_type',
'appointment_type',
'booking_mode',
'open_mode',
'open_time',
'reservation_notice',
'ticket_content',
'booking_method',
'visit_form',
'consultation_hours',
'booking_qr_media',
'address',
'contact_phone',
'lat',
'lng',
'cover_image',
'gallery_media',
'detail_html',
'live_people_count',
'sort',
'is_active',
'is_included_in_stats',
'audit_status',
'audit_remark',
'last_approved_snapshot',
];
public const AUDIT_APPROVED = 'approved';
public const AUDIT_PENDING = 'pending';
public const AUDIT_REJECTED = 'rejected';
// 预约模式常量
public const BOOKING_MODE_TEAM_ONLY = 'team_only';
public const BOOKING_MODE_ALL_REQUIRED = 'all_required';
public const BOOKING_MODE_TEAM_REQUIRED = 'team_required';
/** @var list<string> */
public const SNAPSHOT_KEYS = [
'name', 'venue_type', 'venue_types', 'unit_name', 'district', 'ticket_type',
'appointment_type', 'booking_mode', 'open_mode', 'open_time', 'reservation_notice',
'ticket_content', 'booking_method', 'visit_form', 'consultation_hours', 'booking_qr_media',
'address', 'contact_phone', 'lat', 'lng', 'cover_image', 'gallery_media',
'detail_html', 'live_people_count', 'sort', 'is_active', 'is_included_in_stats',
];
/** @var list<string> */
public const REFERENCE_NAME_COLUMNS = ['id', 'name', 'audit_status', 'last_approved_snapshot'];
/**
* 活动/预约等引用场馆时的 select 列(含展示名所需快照字段)。
*
* @param list<string> $extra
*/
public static function referenceNameSelectString(array $extra = []): string
{
return implode(',', array_values(array_unique(array_merge(self::REFERENCE_NAME_COLUMNS, $extra))));
}
/**
* 作为关联引用展示时使用的场馆名:待审/退回且存在已通过快照则取快照名称。
*/
public function displayNameForReference(): string
{
$current = (string) ($this->attributes['name'] ?? '');
if (! in_array($this->audit_status, [self::AUDIT_PENDING, self::AUDIT_REJECTED], true)) {
return $current;
}
$snap = $this->last_approved_snapshot;
if (is_array($snap) && trim((string) ($snap['name'] ?? '')) !== '') {
return (string) $snap['name'];
}
return $current;
}
public function applyDisplayNameForReference(): self
{
$this->setAttribute('name', $this->displayNameForReference());
return $this;
}
protected $hidden = [
'verify_portal_pin',
];
protected $casts = [
'is_active' => 'boolean',
'is_included_in_stats' => 'boolean',
'gallery_media' => 'array',
'booking_qr_media' => 'array',
'venue_types' => 'array',
'lat' => 'float',
'lng' => 'float',
'sort' => 'integer',
'live_people_count' => 'integer',
'last_approved_snapshot' => 'array',
];
/**
* H5 展示:已启用且(已通过审核,或待审/退回但仍有已通过快照可展示旧版内容)。
*/
public function scopeVisibleOnH5(Builder $query): Builder
{
return $query
->where('is_active', true)
->where(function (Builder $q) {
$q->where('audit_status', self::AUDIT_APPROVED)
->orWhere(function (Builder $sub) {
$sub->whereIn('audit_status', [self::AUDIT_PENDING, self::AUDIT_REJECTED])
->whereNotNull('last_approved_snapshot');
});
});
}
/**
* @return array<string, mixed>|null
*/
public function toH5Payload(): ?array
{
if (! $this->is_active) {
return null;
}
if ($this->audit_status === self::AUDIT_APPROVED) {
return $this->toArray();
}
$snap = $this->last_approved_snapshot;
if (is_array($snap) && $snap !== []) {
$out = $snap;
$out['id'] = $this->id;
return $out;
}
return null;
}
/**
* 场馆管理员修改「已通过」记录前,保存用于 H5 继续展示的旧数据。
*
* @return array<string, mixed>
*/
public function buildAuditSnapshot(): array
{
$out = [];
foreach (self::SNAPSHOT_KEYS as $k) {
$out[$k] = $this->getAttribute($k);
}
return $out;
}
protected static function booted(): void
{
static::saving(function (Venue $v) {
$types = $v->venue_types;
if (is_array($types) && count($types)) {
$clean = array_values(array_filter(array_map('strval', $types), fn ($s) => $s !== ''));
$v->venue_types = $clean;
$v->venue_type = $clean[0] ?? null;
} elseif (! empty($v->venue_type)) {
$v->venue_types = [$v->venue_type];
} else {
$v->venue_type = null;
$v->venue_types = null;
}
});
}
public function activities(): HasMany
{
return $this->hasMany(Activity::class);
}
public function admins(): BelongsToMany
{
return $this->belongsToMany(User::class, 'user_venue')->withTimestamps();
}
public function reservations(): HasMany
{
return $this->hasMany(Reservation::class);
}
}