ticket_service.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. # app/services/ticket_service.py
  2. from datetime import datetime
  3. from redis.asyncio import Redis
  4. from typing import List
  5. from sqlalchemy.orm import Session
  6. from app.utils.search import apply_keyword_search
  7. from app.utils.pagination import paginate
  8. from app.core.biz_exception import NotFoundError, PermissionDeniedError, BizLogicError
  9. from app.models.user import VasUser
  10. from app.models.ticket import VasTicket
  11. from app.models.ticket_message import VasTicketMessage
  12. from app.schemas.ticket import VasTicketCreate
  13. from app.services.notification_service import NotificationService
  14. class TicketService:
  15. @staticmethod
  16. def create(db: Session, data: VasTicketCreate, current_user: VasUser, redis_client: Redis):
  17. rec = VasTicket(**data.dict(), status='pending', created_at=datetime.utcnow())
  18. rec.user_id = current_user.id
  19. db.add(rec)
  20. db.commit()
  21. db.refresh(rec)
  22. print(f"📧 send ticket created notification email")
  23. NotificationService.create(
  24. redis_client=redis_client,
  25. ntype="ticket created",
  26. user_id=current_user.id,
  27. channels=["email"],
  28. template_id="ticket_created",
  29. payload={
  30. "ticket_id": rec.id,
  31. "order_id": rec.order_id
  32. }
  33. )
  34. return rec
  35. @staticmethod
  36. def update_status(db: Session, ticket_id, status, comment, admin_id):
  37. ticket = db.query(VasTicket).filter_by(id=ticket_id).first()
  38. if not ticket:
  39. raise NotFoundError("Ticket not exist")
  40. ticket.status = status
  41. ticket.admin_comment = comment
  42. db.add(
  43. VasTicketMessage(
  44. ticket_id=ticket_id,
  45. sender_type="admin",
  46. sender_id=admin_id,
  47. content=comment
  48. )
  49. )
  50. db.commit()
  51. db.refresh(ticket)
  52. return ticket
  53. @staticmethod
  54. def add_message(
  55. db: Session,
  56. ticket_id: int,
  57. sender_type: str, # "user" | "admin" | "system"
  58. sender_id: str = None,
  59. content: str = "",
  60. attachments: dict = None
  61. ):
  62. # 1️⃣ 校验工单是否存在
  63. ticket = db.query(VasTicket).filter(
  64. VasTicket.id == ticket_id
  65. ).first()
  66. if not ticket:
  67. raise NotFoundError("Ticket not exist")
  68. # 2️⃣ 创建消息
  69. message = VasTicketMessage(
  70. ticket_id=ticket_id,
  71. sender_type=sender_type,
  72. sender_id=sender_id,
  73. content=content,
  74. attachments=attachments,
  75. created_at=datetime.utcnow()
  76. )
  77. # 3️⃣ 写入数据库
  78. db.add(message)
  79. # 4️⃣ 更新工单更新时间(非常重要)
  80. ticket.updated_at = datetime.utcnow()
  81. db.commit()
  82. db.refresh(message)
  83. return message
  84. @staticmethod
  85. def list_messages(
  86. db: Session,
  87. ticket_id: int,
  88. page: int = 1,
  89. size: int = 20
  90. ):
  91. # 1️⃣ 校验 ticket 是否存在
  92. exists = db.query(VasTicket.id).filter(
  93. VasTicket.id == ticket_id
  94. ).first()
  95. if not exists:
  96. raise NotFoundError("Ticket not exist")
  97. # 2️⃣ 查询消息(按时间正序)
  98. query = (
  99. db.query(VasTicketMessage)
  100. .filter(VasTicketMessage.ticket_id == ticket_id)
  101. .order_by(VasTicketMessage.created_at.desc())
  102. )
  103. return paginate(query, page, size)
  104. @staticmethod
  105. def list_by_user(db: Session, user_id: str, page: int=0, size: int=10, keyword: str=None):
  106. query = db.query(VasTicket).filter_by(user_id=user_id)
  107. query = apply_keyword_search(
  108. query=query,
  109. model=VasTicket,
  110. keyword=keyword,
  111. fields=["order_id", "user_id", "reason", "admin_comment"]
  112. )
  113. query = query.order_by(
  114. VasTicket.id.desc()
  115. )
  116. return paginate(query, page, size)
  117. @staticmethod
  118. def list_all(db: Session, page: int=0, size: int=10, keyword: str=None):
  119. query = db.query(VasTicket)
  120. query = apply_keyword_search(
  121. query=query,
  122. model=VasTicket,
  123. keyword=keyword,
  124. fields=["order_id", "user_id", "reason", "admin_comment"]
  125. )
  126. query = query.order_by(
  127. VasTicket.id.desc()
  128. )
  129. return paginate(query, page, size)