help-desk/backend/helpdesk/services/ticket_service.py
2025-12-01 05:43:37 +07:00

114 lines
4.5 KiB
Python

from typing import Optional
from datetime import datetime
from django.db.transaction import atomic
from helpdesk.repositories.ticket_repository import TicketRepository
from helpdesk.models import Ticket, TicketStatus
from django.contrib.auth import get_user_model
class TicketNotFoundError(Exception):
"""Raised when the requested Ticket is not found."""
pass
class NotificationService:
"""Placeholder สำหรับ NotificationService"""
def send_assignment_notification(self, ticket_id, user_id):
pass
class TicketService:
def __init__(self, ticket_repo: Optional[TicketRepository] = None, notification_service=None):
# รองรับ DI ในการทดสอบ — หากไม่ส่งมา จะสร้าง repo ใหม่สำหรับ production
self.ticket_repo = ticket_repo or TicketRepository()
self.notification_service = notification_service
@atomic
def update_ticket_on_new_message(
self,
ticket_id: int,
content: str,
timestamp: datetime,
sender_is_agent: bool,
) -> Ticket:
"""
อัปเดต Ticket เมื่อมีข้อความใหม่เข้ามาใน Chat
- ถ้าไม่พบ Ticket จะโยน TicketNotFoundError
"""
ticket = self.ticket_repo.get_ticket_by_id(ticket_id)
if ticket is None:
raise TicketNotFoundError(f"Ticket with id={ticket_id} not found.")
update_data = {
"last_message_at": timestamp,
"last_message_content": content,
"is_read": True if sender_is_agent else False,
}
return self.ticket_repo.update_ticket(ticket, update_data)
def update_ticket_status(self, ticket_id: int, new_status: str) -> Ticket:
"""อัปเดตสถานะของ Ticket และตรวจสอบ Business Rule"""
ticket = self.ticket_repo.get_ticket_by_id(ticket_id)
if ticket is None:
raise TicketNotFoundError(f"Ticket with id={ticket_id} not found.")
# Business Rule ห้ามเปลี่ยนจาก CLOSED -> OPEN
if ticket.status == TicketStatus.CLOSED and new_status == TicketStatus.OPEN:
raise ValueError("Cannot transition from CLOSED to OPEN")
# ตรวจสอบว่า new_status เป็นค่าที่ถูกต้องใน enum หรือไม่ (เผื่อกรณี View ส่ง string ที่ผิดมา)
if new_status not in TicketStatus:
raise ValueError(f"Invalid status value: {new_status}")
updated_ticket = self.ticket_repo.update_ticket(ticket, {"status": new_status})
# คืน updated_ticket
return updated_ticket
def get_inbox_summary(self):
return self.ticket_repo.get_unified_inbox_list()
def get_ticket_detail(self, ticket_id: int) -> Ticket:
ticket = self.ticket_repo.get_ticket_by_id(ticket_id)
if ticket is None:
raise TicketNotFoundError(f"Ticket with id={ticket_id} not found.")
return ticket
def get_ticket_ref(self, ticket_id: int) -> dict:
ticket = self.ticket_repo.get_ticket_by_id(ticket_id)
return {
"id": ticket.id,
"last_message_at": ticket.last_message_at,
"last_message_content": ticket.last_message_content,
"is_read": ticket.is_read
}
@atomic
def assign_ticket_to_user(self, ticket_id: int, assignee_id: int):
"""
มอบหมาย Ticket ให้กับผู้ใช้ (Agent) และเรียก NotificationService ถ้ามี
"""
ticket = self.ticket_repo.get_ticket_by_id(ticket_id)
if not ticket:
raise TicketNotFoundError(f"Ticket with id={ticket_id} not found.")
# ดึง User Object จาก ID
User = get_user_model()
try:
assigned_user = User.objects.get(pk=assignee_id)
except User.DoesNotExist:
raise ValueError("User not found")
updated_ticket = self.ticket_repo.update_ticket(ticket, {"assigned_to": assigned_user})
if self.notification_service:
self.notification_service.send_assignment_notification(
ticket_id=updated_ticket.id,
user_id=assigned_user.id
)
# คืน updated_ticket
return updated_ticket
ticket_service = TicketService()