114 lines
4.5 KiB
Python
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() |