Files
group_fqcd_jr/app/service/conversation_service.py

81 lines
4.0 KiB
Python

from datetime import UTC, datetime
from uuid import uuid4
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.contracts import RequestContext
from app.core.errors import ConflictAgentError, ResourceNotFoundError
from app.model.audit import InteractionAudit
from app.model.conversation import ConversationFeedback
from app.model.platform import HandoverTicket
from app.repository.conversation_repository import ConversationRepository
from app.service.authorization_service import AuthorizationService
class ConversationService:
def __init__(self, session: AsyncSession) -> None:
self.session = session
self.repository = ConversationRepository(session)
async def messages(
self, session_id: str, context: RequestContext, limit: int
) -> dict[str, object]:
rows = await self.repository.messages(session_id, int(context.user_id), limit)
return {"data": [{"message_id": str(row.id), "role": row.role, "content": row.content,
"created_at": row.created_at.isoformat() + "Z"} for row in rows]}
async def feedback(
self, message_id: int, context: RequestContext, rating: int,
feedback_type: str | None, feedback_content: str | None,
) -> dict[str, object]:
await AuthorizationService.require(context, "conversation:feedback")
async with self.session.begin():
message = await self.repository.message(message_id, int(context.user_id))
if message is None:
raise ResourceNotFoundError("消息不存在")
if await self.repository.feedback(message_id, int(context.user_id)) is not None:
raise ConflictAgentError("消息已反馈")
now = datetime.now(UTC).replace(tzinfo=None)
feedback = ConversationFeedback(
feedback_no=f"fb-{uuid4().hex[:24]}", session_id=message.session_id,
message_id=message_id, customer_id=int(context.user_id), rating=rating,
feedback_type=feedback_type, feedback_content=feedback_content, status="open",
created_at=now, updated_at=now,
)
self.session.add(feedback)
self._audit(context, message.session_id, "conversation.feedback_created",
{"feedback_no": feedback.feedback_no})
return {"data": {"feedback_no": feedback.feedback_no, "status": feedback.status}}
async def handover(
self, session_id: str, context: RequestContext, reason_code: str,
reason_detail: str | None,
) -> dict[str, object]:
await AuthorizationService.require(context, "handover:create")
async with self.session.begin():
messages = await self.repository.messages(session_id, int(context.user_id), 1)
if not messages:
raise ResourceNotFoundError("会话不存在")
now = datetime.now(UTC).replace(tzinfo=None)
ticket = HandoverTicket(
ticket_no=f"ticket-{uuid4().hex[:24]}", session_id=session_id,
customer_id=int(context.user_id), source_agent="customer_service",
source_message_id=messages[0].id, reason_code=reason_code,
reason_detail=reason_detail, status="pending", created_at=now, updated_at=now,
)
self.session.add(ticket)
self._audit(context, session_id, "conversation.transfer_requested",
{"ticket_no": ticket.ticket_no})
return {"data": {"handover_id": ticket.ticket_no, "session_id": session_id,
"status": ticket.status, "created_at": now.isoformat() + "Z"}}
def _audit(
self, context: RequestContext, session_id: str, action: str, detail: dict[str, object]
) -> None:
self.session.add(InteractionAudit(
actor_type="user", actor_id=int(context.user_id), session_id=session_id,
portal=context.portal, action_type=action,
detail={**detail, "trace_id": context.trace_id},
created_at=datetime.now(UTC).replace(tzinfo=None),
))