| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131 |
- <?php
- namespace App\Models;
- use App\Observers\TicketObserver;
- use Auth;
- use Carbon\Carbon;
- use Illuminate\Database\Eloquent\Attributes\ObservedBy;
- use Illuminate\Database\Eloquent\Builder;
- use Illuminate\Database\Eloquent\Model;
- use Illuminate\Database\Eloquent\Relations\BelongsTo;
- use Illuminate\Database\Eloquent\Relations\HasMany;
- /**
- * 工单.
- */
- #[ObservedBy([TicketObserver::class])]
- class Ticket extends Model
- {
- protected $table = 'ticket';
- protected $guarded = [];
- public function scopeUid(Builder $query): Builder
- {
- return $query->whereUserId(Auth::id());
- }
- public function user(): BelongsTo
- {
- return $this->belongsTo(User::class);
- }
- public function admin(): BelongsTo
- {
- return $this->belongsTo(User::class);
- }
- public function reply(): HasMany
- {
- return $this->hasMany(TicketReply::class);
- }
- public function close(): bool
- {
- $this->status = 2;
- return $this->save();
- }
- public function getStatusLabelAttribute(): string
- {
- return match ($this->status) {
- 0 => '<span class="badge badge-lg badge-success">'.trans('common.status.pending').'</span>',
- 1 => '<span class="badge badge-lg badge-danger">'.trans('common.status.reply').'</span>',
- 2 => '<span class="badge badge-lg badge-default">'.trans('common.status.closed').'</span>',
- default => '<span class="badge badge-lg badge-default">'.trans('common.status.unknown').'</span>',
- };
- }
- public static function getAvgFirstResponseData(int $days = 30): array
- {
- $startDate = Carbon::now()->subDays($days);
- $subQuery = TicketReply::query()
- ->select('ticket_id')
- ->selectRaw('MIN(created_at) as first_reply_time')
- ->whereNotNull('admin_id')
- ->groupBy('ticket_id');
- $result = self::query()
- ->joinSub($subQuery, 'reply', 'ticket.id', '=', 'reply.ticket_id')
- ->where('ticket.created_at', '>=', $startDate)
- ->selectRaw('
- COUNT(*) as total_count,
- COALESCE(AVG(TIMESTAMPDIFF(SECOND, ticket.created_at, reply.first_reply_time)), 0) as avg_seconds
- ')
- ->first();
- return [
- 'count' => (int) ($result->total_count ?? 0),
- 'avg_time' => (float) ($result->avg_seconds ?? 0),
- ];
- }
- public static function getTicketStatusStats(): array
- {
- $stats = self::query()->selectRaw('
- COUNT(*) as total,
- SUM(CASE WHEN status = 0 THEN 1 ELSE 0 END) as pending,
- SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) as replied,
- SUM(CASE WHEN status = 2 THEN 1 ELSE 0 END) as closed
- ')->first();
- return [
- 'total' => (int) ($stats->total ?? 0),
- 'pending' => (int) ($stats->pending ?? 0),
- 'replied' => (int) ($stats->replied ?? 0),
- 'closed' => (int) ($stats->closed ?? 0),
- ];
- }
- public static function getTodayTicketCount(): int
- {
- return (int) self::query()
- ->whereDate('created_at', Carbon::today())
- ->count();
- }
- public static function getAvgCloseTime(int $days = 30): ?float
- {
- $startDate = Carbon::now()->subDays($days);
- $subQuery = TicketReply::query()
- ->select('ticket_id')
- ->selectRaw('MAX(created_at) as last_reply_time')
- ->whereNotNull('admin_id')
- ->groupBy('ticket_id');
- $result = self::query()
- ->joinSub($subQuery, 'reply', 'ticket.id', '=', 'reply.ticket_id')
- ->where('ticket.status', 2) // 已关闭的工单
- ->where('ticket.created_at', '>=', $startDate)
- ->selectRaw('
- COALESCE(AVG(TIMESTAMPDIFF(SECOND, ticket.created_at, reply.last_reply_time)), 0) as avg_seconds
- ')
- ->first();
- return (float) ($result->avg_seconds ?? 0);
- }
- }
|