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 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: TicketStatus) -> 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.") # Business Rule: ห้ามเปลี่ยนจาก CLOSED -> OPEN if ticket.status == TicketStatus.CLOSED and new_status == TicketStatus.OPEN: raise ValueError("cannot transition from CLOSED to OPEN") return self.ticket_repo.update_ticket(ticket, {"status": new_status}) 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, assigned_user): """ มอบหมาย 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.") 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 ) return ticket ticket_service = TicketService()