1
0

Ticket.php 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. <?php
  2. namespace App\Models;
  3. use App\Observers\TicketObserver;
  4. use Auth;
  5. use Carbon\Carbon;
  6. use Illuminate\Database\Eloquent\Attributes\ObservedBy;
  7. use Illuminate\Database\Eloquent\Builder;
  8. use Illuminate\Database\Eloquent\Model;
  9. use Illuminate\Database\Eloquent\Relations\BelongsTo;
  10. use Illuminate\Database\Eloquent\Relations\HasMany;
  11. /**
  12. * 工单.
  13. */
  14. #[ObservedBy([TicketObserver::class])]
  15. class Ticket extends Model
  16. {
  17. protected $table = 'ticket';
  18. protected $guarded = [];
  19. public function scopeUid(Builder $query): Builder
  20. {
  21. return $query->whereUserId(Auth::id());
  22. }
  23. public function user(): BelongsTo
  24. {
  25. return $this->belongsTo(User::class);
  26. }
  27. public function admin(): BelongsTo
  28. {
  29. return $this->belongsTo(User::class);
  30. }
  31. public function reply(): HasMany
  32. {
  33. return $this->hasMany(TicketReply::class);
  34. }
  35. public function close(): bool
  36. {
  37. $this->status = 2;
  38. return $this->save();
  39. }
  40. public function getStatusLabelAttribute(): string
  41. {
  42. return match ($this->status) {
  43. 0 => '<span class="badge badge-lg badge-success">'.trans('common.status.pending').'</span>',
  44. 1 => '<span class="badge badge-lg badge-danger">'.trans('common.status.reply').'</span>',
  45. 2 => '<span class="badge badge-lg badge-default">'.trans('common.status.closed').'</span>',
  46. default => '<span class="badge badge-lg badge-default">'.trans('common.status.unknown').'</span>',
  47. };
  48. }
  49. public static function getAvgFirstResponseData(int $days = 30): array
  50. {
  51. $startDate = Carbon::now()->subDays($days);
  52. $subQuery = TicketReply::query()
  53. ->select('ticket_id')
  54. ->selectRaw('MIN(created_at) as first_reply_time')
  55. ->whereNotNull('admin_id')
  56. ->groupBy('ticket_id');
  57. $result = self::query()
  58. ->joinSub($subQuery, 'reply', 'ticket.id', '=', 'reply.ticket_id')
  59. ->where('ticket.created_at', '>=', $startDate)
  60. ->selectRaw('
  61. COUNT(*) as total_count,
  62. COALESCE(AVG(TIMESTAMPDIFF(SECOND, ticket.created_at, reply.first_reply_time)), 0) as avg_seconds
  63. ')
  64. ->first();
  65. return [
  66. 'count' => (int) ($result->total_count ?? 0),
  67. 'avg_time' => (float) ($result->avg_seconds ?? 0),
  68. ];
  69. }
  70. public static function getTicketStatusStats(): array
  71. {
  72. $stats = self::query()->selectRaw('
  73. COUNT(*) as total,
  74. SUM(CASE WHEN status = 0 THEN 1 ELSE 0 END) as pending,
  75. SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) as replied,
  76. SUM(CASE WHEN status = 2 THEN 1 ELSE 0 END) as closed
  77. ')->first();
  78. return [
  79. 'total' => (int) ($stats->total ?? 0),
  80. 'pending' => (int) ($stats->pending ?? 0),
  81. 'replied' => (int) ($stats->replied ?? 0),
  82. 'closed' => (int) ($stats->closed ?? 0),
  83. ];
  84. }
  85. public static function getTodayTicketCount(): int
  86. {
  87. return (int) self::query()
  88. ->whereDate('created_at', Carbon::today())
  89. ->count();
  90. }
  91. public static function getAvgCloseTime(int $days = 30): ?float
  92. {
  93. $startDate = Carbon::now()->subDays($days);
  94. $subQuery = TicketReply::query()
  95. ->select('ticket_id')
  96. ->selectRaw('MAX(created_at) as last_reply_time')
  97. ->whereNotNull('admin_id')
  98. ->groupBy('ticket_id');
  99. $result = self::query()
  100. ->joinSub($subQuery, 'reply', 'ticket.id', '=', 'reply.ticket_id')
  101. ->where('ticket.status', 2) // 已关闭的工单
  102. ->where('ticket.created_at', '>=', $startDate)
  103. ->selectRaw('
  104. COALESCE(AVG(TIMESTAMPDIFF(SECOND, ticket.created_at, reply.last_reply_time)), 0) as avg_seconds
  105. ')
  106. ->first();
  107. return (float) ($result->avg_seconds ?? 0);
  108. }
  109. }