$userId, 'ticket_id' => $ticket->id, 'message' => $message ]); $isAdmin = $userId !== $ticket->user_id; $ticket->reply_status = $isAdmin ? Ticket::REPLY_STATUS_REPLIED : Ticket::REPLY_STATUS_WAITING; $ticket->last_reply_user_id = $userId; if (!$ticketMessage || !$ticket->save()) { throw new \Exception(); } $ticket->setRelation('telegram_source_message', $ticketMessage); DB::commit(); return $ticketMessage; } catch (\Exception $e) { DB::rollback(); return false; } } public function replyByAdmin($ticketId, $message, $userId): void { $ticket = Ticket::where('id', $ticketId)->first(); if (!$ticket) { throw new ApiException('工单不存在'); } $ticketMessage = $this->reply($ticket, $message, $userId); if (!$ticketMessage) { throw new ApiException('工单回复失败'); } $this->notifyAdminReply($ticket, $ticketMessage); } public function notifyAdminReply(Ticket $ticket, TicketMessage $ticketMessage): void { try { HookManager::call('ticket.reply.admin.after', [$ticket, $ticketMessage]); } catch (\Throwable $e) { \Illuminate\Support\Facades\Log::warning('Ticket reply hook failed', ['ticket_id' => $ticket->id]); } try { $this->sendEmailNotify($ticket, $ticketMessage); } catch (\Throwable $e) { \Illuminate\Support\Facades\Log::warning('Ticket email notification could not be queued', ['ticket_id' => $ticket->id]); } } public function createTicket($userId, $subject, $level, $message) { try { DB::beginTransaction(); if (Ticket::where('status', 0)->where('user_id', $userId)->lockForUpdate()->first()) { DB::rollBack(); throw new ApiException('存在未关闭的工单'); } $ticket = Ticket::create([ 'user_id' => $userId, 'subject' => $subject, 'level' => $level, 'reply_status' => Ticket::REPLY_STATUS_WAITING, 'last_reply_user_id' => $userId, ]); if (!$ticket) { throw new ApiException('工单创建失败'); } $ticketMessage = TicketMessage::create([ 'user_id' => $userId, 'ticket_id' => $ticket->id, 'message' => $message ]); if (!$ticketMessage) { DB::rollBack(); throw new ApiException('工单消息创建失败'); } $ticket->setRelation('telegram_source_message', $ticketMessage); DB::commit(); return $ticket; } catch (\Exception $e) { DB::rollBack(); throw $e; } } // 半小时内不再重复通知 private function sendEmailNotify(Ticket $ticket, TicketMessage $ticketMessage) { $user = User::find($ticket->user_id); if (!$user || !filter_var($user->email, FILTER_VALIDATE_EMAIL)) { return; } $cacheKey = 'ticket_sendEmailNotify_' . $ticket->user_id; if (!Cache::get($cacheKey)) { Cache::put($cacheKey, 1, 1800); SendEmailJob::dispatch([ 'email' => $user->email, 'subject' => '您在' . admin_setting('app_name', 'XBoard') . '的工单得到了回复', 'template_name' => 'notify', 'purpose' => 'ticket_reply', 'user_id' => $user->id, 'template_value' => [ 'name' => admin_setting('app_name', 'XBoard'), 'url' => admin_setting('app_url'), 'content' => "主题:{$ticket->subject}\r\n回复内容:{$ticketMessage->message}" ] ])->afterCommit(); } } }