from typing import Literal from pydantic import BaseModel, Field class TicketCreateRequest(BaseModel): workspace_code: str = Field(default="", max_length=50) requester_id: str = Field(..., min_length=1, max_length=100) requester_tenant_id: str = Field(..., min_length=1, max_length=100) requester_tenant_ids: list[str] = Field(default_factory=list) requester_contact: str | None = Field(default=None, max_length=100) requester_email: str | None = Field(default=None, max_length=320) requester_name: str | None = Field(default=None, max_length=100) requester_department: str | None = Field(default=None, max_length=100) requester_phone_number: str | None = Field(default=None, max_length=50) title: str = Field(..., min_length=1, max_length=255) description: str = Field(..., min_length=1) category_code: str | None = Field(default=None, max_length=20) ticket_type: str = Field(default="GENERAL") is_secret: bool = False requires_approval: bool = False extra_fields: dict[str, str] = Field(default_factory=dict) class TicketUpdateRequest(BaseModel): title: str | None = Field(default=None, min_length=1, max_length=255) description: str | None = Field(default=None, min_length=1) requester_contact: str | None = Field(default=None, max_length=100) is_secret: bool | None = None extra_fields: dict[str, str] | None = None class TicketCreateResponse(BaseModel): ticket_id: int workspace_code: str status_code: str feedback_status: str sync_status: str message: str FeedbackStatus = Literal["INIT", "ON_REVIEW", "DETAILED_REVIEW", "IN_PROGRESS", "RESOLVED", "PENDING"] class FeedbackStatusUpdateRequest(BaseModel): feedback_status: FeedbackStatus class SupportFeedbackStatusRecord(BaseModel): feedback_id: int feedback_status: FeedbackStatus updated_at: str class SupportFeedbackMetrics(BaseModel): project_id: int channel_id: int | None = None today_first_count: int answer_waiting_count: int issue_linked_count: int issue_link_rate: float average_processing_minutes: float | None = None class WorkspaceSummary(BaseModel): workspace_code: str workspace_name: str workspace_type: str class WorkspaceFormField(BaseModel): field_code: str label: str field_type: str required: bool = False options: list[dict[str, str | int]] = Field(default_factory=list) class WorkspaceFormTemplateResponse(BaseModel): workspace_code: str workspace_name: str requires_approval: bool fields: list[WorkspaceFormField] class SupportActivityItem(BaseModel): id: str label: str detail: str timestamp: str class SupportAttachmentRecord(BaseModel): attachment_id: int = Field(description="첨부파일 식별자") original_file_name: str = Field(description="사용자가 업로드한 원본 파일명") mime_type: str | None = Field(default=None, description="MIME 타입") file_size: int | None = Field(default=None, description="파일 크기(bytes)") created_at: str = Field(description="첨부파일 생성 시각") class TicketCommentCreateRequest(BaseModel): requester_id: str = Field( ..., min_length=1, max_length=100, description="인증된 작성자 식별자. API 서버가 로그인 사용자 정보로 덮어쓴다.", ) requester_tenant_id: str = Field( ..., min_length=1, max_length=100, description="인증된 작성자 테넌트 식별자. API 서버가 로그인 사용자 정보로 덮어쓴다.", ) author_name: str | None = Field( default=None, max_length=100, description="작성자 표시명. 인증 사용자 정보로 서버가 결정한다.", ) content: str = Field(default="", description="댓글 본문") is_internal: bool = Field( default=False, description="서버 내부용 필드. 일반 댓글 API에서는 무시되며 내부 메모 API가 true로 설정한다.", ) class TicketCommentUpdateRequest(BaseModel): requester_id: str = Field(..., min_length=1, max_length=100) requester_tenant_id: str = Field(..., min_length=1, max_length=100) content: str = Field(..., min_length=1) class InternalMemoCreateRequest(BaseModel): content: str = Field(..., min_length=1, description="관리자만 조회할 내부 메모 본문") class InternalMemoUpdateRequest(BaseModel): content: str = Field(..., min_length=1, description="수정할 내부 메모 본문") class TicketIssueLinkStatusSyncRequest(BaseModel): linked: bool class TicketAssigneeCandidate(BaseModel): user_id: str tenant_id: str name: str | None = None email: str | None = None department: str | None = None phone_number: str | None = None role_code: str class TicketAssigneeUpdateRequest(BaseModel): assignee_id: str | None = None assignee_tenant_id: str | None = None class SupportTicketCommentRecord(BaseModel): comment_id: int = Field(description="댓글 또는 내부 메모 식별자") ticket_id: int = Field(description="지원 요청 식별자") author_id: str = Field(description="작성자 식별자") author_tenant_id: str = Field(description="작성자 테넌트 식별자") author_name: str = Field(description="작성자 표시명") content: str = Field(description="댓글 또는 내부 메모 본문") is_internal: bool = Field(description="관리자 전용 내부 메모 여부") comment_type: str = Field(default="COMMENT", description="COMMENT 또는 INTERNAL_MEMO") created_at: str = Field(description="작성 시각") updated_at: str = Field(description="수정 시각") edited_at: str | None = Field(default=None, description="마지막 수정 시각") attachments: list[SupportAttachmentRecord] = Field(default_factory=list) can_edit: bool = False can_delete: bool = False class TicketDeleteResponse(BaseModel): deleted: bool = Field(description="삭제 처리 여부") ticket_id: int = Field(description="지원 요청 식별자") comment_id: int | None = Field(default=None, description="삭제된 댓글 또는 내부 메모 식별자") feedback_id: int | None = Field(default=None, description="연결된 ABC 피드백 식별자") class SupportTicketRecord(BaseModel): ticket_id: int workspace_code: str workspace_name: str title: str description: str requester_id: str requester_tenant_id: str requester_tenant_ids: list[str] = Field(default_factory=list) requester_contact: str requester_email: str | None requester_name: str | None requester_department: str | None requester_phone_number: str | None category_code: str ticket_type: str requires_approval: bool status_code: str feedback_status: str approval_status: str sync_status: str issue_link_status: str external_issue_status: str | None = None assignee_id: str | None = None assignee_tenant_id: str | None = None assignee_name: str | None = None assignee_email: str | None = None is_secret: bool created_at: str updated_at: str extra_fields: dict[str, str] attachments: list[SupportAttachmentRecord] = Field(default_factory=list) activity: list[SupportActivityItem] class TicketMutationResponse(BaseModel): ticket_id: int status_code: str feedback_status: str approval_status: str issue_link_status: str external_issue_status: str | None = None updated_at: str